StarWars
GraphQL 'Star Wars' example using GraphQL for .NET, ASP.NET Core, Entity Framework Core
Install / Use
/learn @JacekKosciesza/StarWarsREADME
GraphQL 'Star Wars' example using GraphQL for .NET, ASP.NET Core, Entity Framework Core
Examples
-
Basic - simple 'Hello GraphQL!' example based on console version from GraphQL for .NET on GitHub, but using ASP.NET Core, Entity Framework Core and some best practices, patterns and principles.
-
Advanced - GraphQL queries and mutations with full 'Star Wars' database (see GraphQL Specification by Facebook and GraphQL.js - reference implementation)
Roadmap
- [x] Basic
- [x] Simple tutorial (step/screenshot/code)
- [ ] Detailed tutorial (steps explanation)
- [x] 3-Layers (Api, Core, Data) architecture
- [x] DDD (Domain Driven Design) hexagonal architecture
- [x] Dependency Inversion (deafult ASP.NET Core IoC container)
- [x] GraphQL controller
- [x] In Memory 'Droid' Repository
- [x] Entity Framework 'Droid' Repository
- [x] Automatic database creation
- [x] Seed database data
- [x] EF Migrations
- [x] GraphiQL
- [x] Unit Tests
- [x] Visual Studio 2017 RC upgrade
- [x] Integration Tests
- [x] Logs
- [x] Code Coverage
- [x] Continous Integration
- [ ] Advanced
- [x] Full 'Star Wars' database (Episodes, Characters, Planets, Humans etc.)
- [x] Base/generic repository
- [x] Visual Studio 2017 RTM upgrade
- [x] Repositories
- [ ] GraphQL queries
- [ ] GraphQL mutations
- [ ] Docker
- [ ] PWA (Progressive Web App)
- [ ] Identity microservice
- [ ] Angular frontend
- [ ] Apollo GraphQL Client for Angular
- [ ] Service Worker
- [ ] IndexedDB
- ...
Tutorials
Basic
-
Create 'StarWars' empty solution

-
Add 'ASP.NET Core Web Application (.NET Core)' project named 'StarWars.Api'

-
Select Web API template

-
Update all NuGet packages

-
Update project.json with correct runtime
"runtimes": {
"win10-x64": { }
}
-
Install GraphQL NuGet package

-
Create 'StarWars.Core' project

-
Create 'Droid' model
namespace StarWars.Core.Models
{
public class Droid
{
public int Id { get; set; }
public string Name { get; set; }
}
}
- Create 'DroidType' model
using GraphQL.Types;
using StarWars.Core.Models;
namespace StarWars.Api.Models
{
public class DroidType : ObjectGraphType<Droid>
{
public DroidType()
{
Field(x => x.Id).Description("The Id of the Droid.");
Field(x => x.Name, nullable: true).Description("The name of the Droid.");
}
}
}
- Create 'StarWarsQuery' model
using GraphQL.Types;
using StarWars.Core.Models;
namespace StarWars.Api.Models
{
public class StarWarsQuery : ObjectGraphType
{
public StarWarsQuery()
{
Field<DroidType>(
"hero",
resolve: context => new Droid { Id = 1, Name = "R2-D2" }
);
}
}
}
- Create 'GraphQLQuery' model
namespace StarWars.Api.Models
{
public class GraphQLQuery
{
public string OperationName { get; set; }
public string NamedQuery { get; set; }
public string Query { get; set; }
public string Variables { get; set; }
}
}
- Create 'GraphQLController'
using GraphQL;
using GraphQL.Types;
using Microsoft.AspNetCore.Mvc;
using StarWars.Api.Models;
using System.Threading.Tasks;
namespace StarWars.Api.Controllers
{
[Route("graphql")]
public class GraphQLController : Controller
{
[HttpPost]
public async Task<IActionResult> Post([FromBody] GraphQLQuery query)
{
var schema = new Schema { Query = new StarWarsQuery() };
var result = await new DocumentExecuter().ExecuteAsync(_ =>
{
_.Schema = schema;
_.Query = query.Query;
}).ConfigureAwait(false);
if (result.Errors?.Count > 0)
{
return BadRequest();
}
return Ok(result);
}
}
}
-
Test using Postman

-
Create 'IDroidRepository' interface
using StarWars.Core.Models;
using System.Threading.Tasks;
namespace StarWars.Core.Data
{
public interface IDroidRepository
{
Task<Droid> Get(int id);
}
}
-
Create 'StarWars.Data' project

-
Create in memory 'DroidRepository'
using StarWars.Core.Data;
using System.Collections.Generic;
using System.Threading.Tasks;
using StarWars.Core.Models;
using System.Linq;
namespace StarWars.Data.InMemory
{
public class DroidRepository : IDroidRepository
{
private List<Droid> _droids = new List<Droid> {
new Droid { Id = 1, Name = "R2-D2" }
};
public Task<Droid> Get(int id)
{
return Task.FromResult(_droids.FirstOrDefault(droid => droid.Id == id));
}
}
}
- Use 'IDroidRepository' in StarWarsQuery
using GraphQL.Types;
using StarWars.Core.Data;
namespace StarWars.Api.Models
{
public class StarWarsQuery : ObjectGraphType
{
private IDroidRepository _droidRepository { get; set; }
public StarWarsQuery(IDroidRepository _droidRepository)
{
Field<DroidType>(
"hero",
resolve: context => _droidRepository.Get(1)
);
}
}
}
- Update creation of StarWarsQuery in GraphQLController
// ...
public async Task<IActionResult> Post([FromBody] GraphQLQuery query)
{
var schema = new Schema { Query = new StarWarsQuery(new DroidRepository()) };
// ...
-
Test using Postman
-
Configure dependency injection in Startup.cs
// ...
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddTransient<StarWarsQuery>();
services.AddTransient<IDroidRepository, DroidRepository>();
}
// ...
- Use constructor injection of StarWarsQuery in GraphQLController
// ...
public class GraphQLController : Controller
{
private StarWarsQuery _starWarsQuery { get; set; }
public GraphQLController(StarWarsQuery starWarsQuery)
{
_starWarsQuery = starWarsQuery;
}
[HttpPost]
public async Task<IActionResult> Post([FromBody] GraphQLQuery query)
{
var schema = new Schema { Query = _starWarsQuery };
// ...
-
Add Microsoft.EntityFrameworkCore and Microsoft.EntityFrameworkCore.SqlServer Nuget packages to StarWars.Data project

-
Create StarWarsContext
using Microsoft.EntityFrameworkCore;
using StarWars.Core.Models;
namespace StarWars.Data.EntityFramework
{
public class StarWarsContext : DbContext
{
public StarWarsContext(DbContextOptions options)
: base(options)
{
Database.EnsureCreated();
}
public DbSet<Droid> Droids { get; set; }
}
}
- Update 'appsetting.json' with database connection
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},
"ConnectionStrings": {
"StarWarsDatabaseConnection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=StarWars;Integrated Security=SSPI;integrated security=true;MultipleActiveResultSets=True;"
}
}
- Create EF droid repository
using StarWars.Core.Data;
using System.Threading.Tasks;
using StarWars.Core.Models;
using Microsoft.EntityFrameworkCore;
namespace StarWars.Data.EntityFramework.Repositories
{
public class DroidRepository : IDroidRepository
{
private StarWarsContext _db { get; set; }
public DroidRepository(StarWarsContext db)
{
_db = db;
}
public Task<Droid> Get(int id)
{
return _db.Droids.FirstOrDefaultAsync(droid => droid.Id == id);
}
}
}
- Create seed data as an extension to StarWarsContext
using StarWars.Core.Models;
using System.Linq;
namespace StarWars.Data.EntityFramework.Seed
{
public static class StarWarsSeedData
{
public static void EnsureSeedData(this StarWarsContext db)
{
if (!db.Droids.Any())
{
var droid = new Droid
{
Name = "R2-D2"
};
db.Droids.Add(droid);
db.SaveChanges();
}
}
}
}
- Configure dependency injection a
