In this article, we will create a simple CRUD (Create, Read, Update, Delete) using GraphQL in Node.js
I'll be using the following tools :
- Express
- Typescript
- Apollo Server for the GraphQL engine
- Sequelize
Javascript environment is very opinionated you can use other tools to achieve the same goal. But the idea is similiar.
I assume that you're already familiar in Node.js development. So, I won't explain further on the project setup. I want to focus on GraphQL explanation. If you want to see my setup you can check it out on my Github repo below.
I have this model
export interface MovieModel extends Model {
id: number;
title: string;
description: string;
posterUrl: string;
}
We will create GraphQL based on the MovieModel.
Schema
In GraphQL, you need to define a schema. The schema is like a blueprint of how GraphQL API can be accessed. You can learn more about GraphQL schema here.
There are two main operations in GraphQL: Query and Mutation. Query is read-only operation. It does not modify any data. Mutation is request for modifying data. It's used to create, update or delete data on the server. You need to define your operations in schema.
There are other operation types too such as Subscription. But, in this article I won't dive further other operations.
Okay, we need to create root schema first, it looks like this.
export const rootSchema = `#graphql
type Query {
root: String
}
type Mutation {
root: String
}
`;
And then we create Movie schema.
export const movieSchema = `#graphql
type Movie {
id: Int!
title: String!
description: String
posterUrl: String
}
extend type Query {
getAllMovies: [Movie]
getOneMovie(id: Int!): Movie
}
extend type Mutation {
createMovie(title: String!, description: String, posterUrl: String): Movie
updateMovie(id: Int, title: String, description: String, posterUrl: String): Movie
deleteMovie(id: Int!): String
}
`
As you can see, we can define custom data type on GraphQL schema. In this case, I created Movie type that has same attributes as the MovieModel.
After that we need to define our operations.
I created two Query operations. getAllMovies and getOneMovie.
getAllMovies doesn't have any input, but it will return an array of Movie.
getOneMovie have one input, id with an integer type. The exclamation mark (!) indicates that the input is required.
getOneMovie will only return one Movie data. That's why it doesn't have square brackets.
And then I created three mutations. Basically it's for create, update and delete.
There's no different in syntax between Query and Mutation.
Resolver
A resolver is the logical function when a GraphQL API is called. The resolver function must has same name as defined in the schema.
Here's example of Movie Resolver
const MovieResolver = {
Query: {
async getAllMovies(root, input, context) {
return Movie.findAll();
},
async getOneMovie(root, input, context){
return Movie.findByPk(input.id);
}
},
Mutation : {
async createMovie(root, input, context) {
const { title, description, posterUrl } = input;
return Movie.create({ title, description, posterUrl });
},
async updateMovie(root, input, context) {
const id = input.id;
const movie = input;
await Movie.update(movie, {
where: { id: id }
});
return {id: id, ...movie};
},
async deleteMovie(root, {id}, context){
const deleteRes = await Movie.destroy({
where: {id: id}
});
if(deleteRes){
return `Movie id:${id} has been deleted`;
}else{
return `Can't delete Movie id:${id}`;
}
},
}
}
export default MovieResolver;
To put it simply, Resolver is like Controller in MVC design pattern.
Initializing Apollo Server
The final touch is to initialize Apollo Server in main.ts
async function startServer(){
const app = express();
const schema = makeExecutableSchema({
resolvers: [MovieResolver],
typeDefs: [rootSchema, movieSchema],
});
const apolloServer = new ApolloServer({
schema: schema,
introspection: true,
});
await apolloServer.start();
app.use(
cors(),
bodyParser.json(),
expressMiddleware(apolloServer)
)
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}....`);
});
}
startServer();
makeExecutableSchema is a function to register our resolvers and schema.
introspection is feature in Apollo to enable GraphQL Server to get the specification details of schema. It's useful in development stage. But, it should be disabled in production for security purpose.
Apollo has built in GUI GraphQL Client. You can access it in /graphql
Here's an example of query and mutation request on Apollo GraphQL Client
That's it for now!
You can access my code here
Top comments (0)