Πώς να δημιουργήσετε ένα εκπληκτικό γρήγορο API GraphQL με Node.js, MongoDB και Fastify

Αυτό το σεμινάριο είναι το δεύτερο μέρος μιας σειράς τεσσάρων τμημάτων, το οποίο στοχεύει να σας μεταφέρει από το μηδέν στην ανάπτυξη μιας πλήρως λειτουργικής εφαρμογής πλήρους στοίβας.

  • Μέρος 1: Πώς να δημιουργήσετε απίστευτα γρήγορα REST API με Node.js, MongoDB, Fastify και Swagger
  • Μέρος 2: Πώς να δημιουργήσετε ένα απίστευτα γρήγορο API GraphQL με Node.js, MongoDB, Fastify και GraphQL! (Είστε εδώ.)
  • Μέρος 3: Σύζευξη Vue.js με GraphQL API .
  • Μέρος 4: Ανάπτυξη ενός API GraphQL και Vue.js εφαρμογή frontend .

Το πρώτο μέρος της σειράς είναι διαθέσιμο εδώ και ο πηγαίος κώδικας για την εφαρμογή βρίσκεται εδώ.

Σε αυτό το μέρος θα επανεξετάσουμε τα μοντέλα , τους ελεγκτές και τις διαδρομές από το πρώτο μέρος και στη συνέχεια θα ενσωματώσουμε το GraphQL στην εφαρμογή. Ως μπόνους θα χρησιμοποιήσουμε επίσης το Faker.jsγια να δημιουργήσετε κάποια ψεύτικα δεδομένα και να δημιουργήσετε τη βάση δεδομένων .

Εισαγωγή:

Το GraphQL είναι μια γλώσσα ερωτήματος για API και ένας χρόνος εκτέλεσης για την εκπλήρωση αυτών των ερωτημάτων με τα υπάρχοντα δεδομένα σας.

Κάθε ερώτημα GraphQL περνά από τρεις φάσεις: τα ερωτήματα αναλύονται, επικυρώνονται και εκτελούνται.

Το GraphQL παρέχει μια πλήρη και κατανοητή περιγραφή των δεδομένων στο API σας, δίνει στους πελάτες τη δυνατότητα να ζητούν ακριβώς αυτό που χρειάζονται, διευκολύνει την εξέλιξη των API με την πάροδο του χρόνου και επιτρέπει ισχυρά εργαλεία προγραμματιστών. Μάθε περισσότερα.

Προαπαιτούμενα…

Αν έχετε ολοκληρώσει το πρώτο μέρος αυτής της σειράς, θα πρέπει να είστε ενημερωμένοι με αρχάριες / ενδιάμεσες γνώσεις JavaScript , Node.js, Fastify.JS και MongoDB (Mongoose).

Για να ακολουθήσετε, θα χρειαστεί να ολοκληρώσετε το πρώτο μέρος αυτής της σειράς ή να πάρετε τον κωδικό από το Git, αν και θα συνιστούσα ανεπιφύλακτα τουλάχιστον να παρακάμψετε το πρώτο μέρος.

Ας αρχίσουμε!

Κλωνοποιήστε το repo για το πρώτο μέρος (παραλείψτε αυτό το βήμα εάν ακολουθήσατε το πρώτο μέρος και συνεχίζετε με τον δικό σας κωδικό) ανοίγοντας το τερματικό σας , μεταβαίνοντας στον κατάλογο του έργου σας καιεκτελώντας καθεμία από τις ακόλουθες γραμμές κώδικα:

git clone //github.com/siegfriedgrimbeek/fastify-api.git cd fastify-api

Τώρα λοιπόν που έχουμε ένα αντίγραφο της βάσης κώδικα, θα ενημερώσουμε τα πακέτα και το package.jsonαρχείο μας εκτελώντας τον ακόλουθο κώδικα:

sudo npm i -g npm-check-updates ncu -u npm install

Πρώτα εγκαθιστούμε παγκοσμίως το πακέτο npm " npm-check-ενημερώσεις " και μετά χρησιμοποιούμε αυτό το πακέτο για να ενημερώσουμε αυτόματα το package.jsonαρχείο μας με τις πιο πρόσφατες εκδόσεις πακέτων και μετά εγκαθιστούμε / ενημερώσουμε όλες τις μονάδες npm εκτελώντας npm install.

Αυτό γίνεται για να διασφαλιστεί ότι όλοι όσοι ολοκληρώνουν το σεμινάριο εργάζονται με τις ίδιες εκδόσεις πακέτων.

Αναμορφώστε τον διακομιστή μας και ξεκινήστε την εφαρμογή!

Όπως συμβαίνει με όλες τις λύσεις λογισμικού, καθώς η λύση μεγαλώνει, οι προγραμματιστές συχνά πρέπει να επανεξετάσουν και να αναδιαμορφώσουν τον κώδικα.

Στον srcκατάλογο θα δημιουργήσουμε ένα νέο αρχείο που ονομάζεται server.js:

cd src touch server.js

Προσθέστε τον ακόλουθο κωδικό κώδικα στο server.jsαρχείο:

// Require the fastify framework and instantiate it const fastify = require('fastify')({ logger: true }) // Require external modules const mongoose = require('mongoose') // Connect to DB mongoose .connect('mongodb://localhost/mycargarage') .then(() => console.log('MongoDB connected...')) .catch(err => console.log(err)) module.exports = fastify

Τώρα έχουμε εξαγάγει τη λογική που ξεκινά το διακομιστή στο server.jsαρχείο, επιτρέποντάς μας να επαναχρησιμοποιήσουμε αυτόν τον κώδικα καθ 'όλη τη διάρκεια του έργου.

Στη συνέχεια πρέπει να ενημερώσουμε το index.jsαρχείο μας στον srcκατάλογο:

 // Import Server const fastify = require('./server.js') // Import Routes const routes = require('./routes') // Import Swagger Options const swagger = require('./config/swagger') // Register Swagger fastify.register(require('fastify-swagger'), swagger.options) // Loop over each route routes.forEach((route, index) => { fastify.route(route) }) // Run the server! const start = async () => { try { await fastify.listen(3000, '0.0.0.0') fastify.swagger() fastify.log.info(`server listening on ${fastify.server.address().port}`) } catch (err) { fastify.log.error(err) process.exit(1) } } start()

Θα επανεξετάσουμε το index.jsαρχείο, μόλις εγκαταστήσουμε και διαμορφώσουμε το GraphQL.

Ξεκινήστε το διακομιστή Fastify εκτελώντας τον ακόλουθο κώδικα στο τερματικό σας :

npm start

Σημειώστε ότι δεν υπάρχει προεπιλεγμένη ρύθμιση διαδρομής, οπότε προς το παρόν, η πλοήγηση στο // localhost: 3000 / θα έχει ως αποτέλεσμα ο διακομιστής να επιστρέψει ένα σφάλμα 404 που είναι σωστό.

Ξεκινήστε το MongoDB και ενημερώστε τα μοντέλα

Ας επεκτείνουμε το υπάρχον μοντέλο για να συμπεριλάβουμε επίσης τις Υπηρεσίες και τους Ιδιοκτήτες. Το παρακάτω διάγραμμα δείχνει τις σχέσεις μεταξύ των συλλογών:

  • Ένα αυτοκίνητο μπορεί να έχει έναν ιδιοκτήτη.
  • Ένας ιδιοκτήτης μπορεί να έχει πολλά αυτοκίνητα.
  • Ένα αυτοκίνητο μπορεί να έχει πολλές υπηρεσίες.

Επανεξετάστε το Car.jsαρχείο στον modelsκατάλογο και ενημερώστε το ως εξής:

// External Dependancies const mongoose = require("mongoose") const ObjectId = mongoose.Schema.Types.ObjectId const carSchema = new mongoose.Schema({ title: String, brand: String, price: String, age: Number, owner_id: ObjectId }) module.exports = mongoose.model("Car", carSchema)

Δημιουργία δύο νέα αρχεία στον modelsκατάλογο, Owner.js και Service.jsκαι προσθέστε τον ακόλουθο κώδικα για τα αρχεία, αντίστοιχα:

Owner.js

// External Dependancies const mongoose = require('mongoose') const ownerSchema = new mongoose.Schema({ firstName: String, lastName: String, email: String }) module.exports = mongoose.model('Owner', ownerSchema)

Service.js

// External Dependancies const mongoose = require("mongoose") const ObjectId = mongoose.Schema.Types.ObjectId const serviceSchema = new mongoose.Schema({ car_id: ObjectId, name: String, date: String }) module.exports = mongoose.model("Service", serviceSchema) view rawService.js hosted with ❤ by GitHub

Δεν υπάρχουν νέες έννοιες που χρησιμοποιούνται στον παραπάνω κώδικα. Μόλις δημιουργήσαμε στάνταρ Mongoose Schemas, όπως και με το Car.jsμοντέλο.

Επανεξετάστε το χειριστήριο αυτοκινήτου και δημιουργήστε τους πρόσθετους ελεγκτές

Υπάρχουν μερικές μικρές αλλαγές στο, carController.jsεπομένως, μεταβείτε στον controllersκατάλογο και ενημερώστε το αρχείο σας σύμφωνα με τα παρακάτω:

// External Dependancies const boom = require('boom') // Get Data Models const Car = require('../models/Car') // Get all cars exports.getCars = async () => { try { const cars = await Car.find() return cars } catch (err) { throw boom.boomify(err) } } // Get single car by ID exports.getSingleCar = async req => { try { const id = req.params === undefined ? req.id : req.params.id const car = await Car.findById(id) return car } catch (err) { throw boom.boomify(err) } } // Add a new car exports.addCar = async req => { try { const car = new Car(req) const newCar = await car.save() return newCar } catch (err) { throw boom.boomify(err) } } // Update an existing car exports.updateCar = async req => { try { const id = req.params === undefined ? req.id : req.params.id const updateData = req.params === undefined ? req : req.params const update = await Car.findByIdAndUpdate(id, updateData, { new: true }) return update } catch (err) { throw boom.boomify(err) } } // Delete a car exports.deleteCar = async req => { try { const id = req.params === undefined ? req.id : req.params.id const car = await Car.findByIdAndRemove(id) return car } catch (err) { throw boom.boomify(err) } }

Δημιουργήστε δύο νέα αρχεία στον controllersκατάλογο serviceController.jsκαι ownerController.js, και προσθέστε τον ακόλουθο κώδικα στα αρχεία αντίστοιχα:

serviceController.js

// External Dependancies const boom = require('boom') // Get Data Models const Service = require('../models/Service') // Get single service ID exports.getSingleService = async req => { try { const id = req.params === undefined ? req.id : req.params.id const service = await Service.findById(id) return service } catch (err) { throw boom.boomify(err) } } // Get single car's services exports.getCarsServices = async req => { try { const id = req.params === undefined ? req.id : req.params.id const services = await Service.find({ car_id: id }) return services } catch (err) { throw boom.boomify(err) } }

ownerController.js

// External Dependancies const boom = require('boom') // Get Data Models const Owner = require('../models/Owner') const Car = require('../models/Car') // Get all owners exports.getOwner = async () => { try { const owners = await Owner.find() return owners } catch (err) { throw boom.boomify(err) } } // Get single owner by ID exports.getSingleOwner = async req => { try { const id = req.params === undefined ? req.id : req.params.id const owner = await Owner.findById(id) return owner } catch (err) { throw boom.boomify(err) } } // Get single owner's cars exports.getOwnersCars = async req => { try { const id = req.params === undefined ? req.id : req.params.id const cars = await Car.find({ owner_id: id }) return cars } catch (err) { throw boom.boomify(err) } }

Η μεγαλύτερη αλλαγή στους ελεγκτές είναι πώς παίρνουμε τις παραμέτρους:

const id = req.params === undefined ? req.id : req.params.id const updateData = req.params === undefined ? req : req.params

Ο παραπάνω κώδικας ονομάζεται « τελεστής υπό όρους » και χρησιμοποιείται ως συντομογραφία για την ακόλουθη δήλωση εάν:

let id if (req.params === undefined) { id = req.id } else { id = req.params.id }

Χρησιμοποιούμε τον τριμερή τελεστή για να ικανοποιήσουμε αιτήματα τόσο από το REST API όσο και από το GraphQL API , καθώς έχουν μια ελαφρώς διαφορετική εφαρμογή.

Ώρα να δημιουργήσετε τη βάση δεδομένων με κάποια ψεύτικα δεδομένα!

In the src directory let’s create a new directory and file by running the following code:

mkdir helpers touch seed.js

Add the following code to the seed.js file:

 // Import external dependancies const faker = require('faker') const boom = require('boom') // Import internal dependancies const fastify = require('../server.js') // Fake data const cars = [ { name: 'Tesla', models: ['S', 'E', 'X', 'Y'] }, { name: 'Mercedes', models: ['GLA', 'GLC', 'GLE', 'GLS'] }, { name: 'BMW', models: ['X4', 'Z3', 'M2', '7'] }, { name: 'Audi', models: ['A1', 'A3', 'A4', 'A5'] }, { name: 'Ford', models: ['Fiesta', 'Focus', 'Fusion', 'Mustang'] } ] const serviceGarages = ['A++ Auto Services', "Gary's Garage", 'Super Service', 'iGarage', 'Best Service'] // Get Data Models const Car = require('../models/Car') const Owner = require('../models/Owner') const Service = require('../models/Service') // Fake data generation functions const generateOwnerData = () => { let ownerData = [] let i = 0 while (i  { let carData = [] let i = 0 while (i  { let serviceData = [] let i = 0 while (i  { try { const owners = await Owner.insertMany(generateOwnerData()) const ownersIds = owners.map(x => x._id) const cars = await Car.insertMany(generateCarData(ownersIds)) const carsIds = cars.map(x => x._id) const services = await Service.insertMany(generateServiceData(carsIds)) console.log(` Data successfully added: - ${owners.length} owners added. - ${cars.length} cars added. - ${services.length} services added. `) } catch (err) { throw boom.boomify(err) } process.exit() }, err => { console.log('An error occured: ', err) process.exit() } )

Let’s break down this mountain of code:

First we import two external libraries, Faker.jswhich is used to generate fake data and Boom, which is used to throw http friendly error objects.

Then we import the server.js file which will spin up an instance of our server allowing us to interact with the models.

We then declare two arrays with fake data, cars and serviceGarages.

Then we import the models and declare three functions (generateOwnerData, generateCarData, generateServiceData) which each return an array of objects with the owner, car and service data respectively.

Once the Fastify.js instance is ready we use the Mongoose insertMany() function to insert the generated arrays into the database. The function then returns an array of objects containing the original object data and ids of the each record.

We use the JavaScript Map function to create an array of idsowners and cars arrays. We use the ownersIDs array for when generating car data and we use the carsIds array when generating service data, they are passed into the respective functions and then values are randomly selected from them.

Lastly we need to install the Faker.js package and add the seed task to our package.json file.

We can add the Faker.js package by navigating to the root directory and running the following code:

npm i faker -D

We then add the following to the package.json file:

... "scripts": { ... "seed": "node ./src/helpers/seed.js" }, ...

That’s it! We can now run our seeding script from the project root directory with the following code:

npm run seed

If you are using MongoDB Compass (you should), you will see the data in your database:

GraphQL installation, setup and testing

Let’s get started by navigating to the root directory and running the following code:

npm i fastify-gql graphql

The above installs GraphQL and the Fastify barebone GraphQL adapter.

Navigate to the src directory and run the following code:

mkdir schema cd shema touch index.js

Navigate to the src directory update the index.js file with the following:

// Import Server const fastify = require('./server.js') // Import external dependancies const gql = require('fastify-gql') // Import GraphQL Schema const schema = require('./schema') // Register Fastify GraphQL fastify.register(gql, { schema, graphiql: true }) ... end here // Import Routes const routes = require('./routes')

With the above code we require the Fastify GraphQL Adapter, import the schema and register the GraphQl Adapter with Fastify.

We register the schema and enable GraphiQL, an in-browser IDE for exploring GraphQL.

Navigate to the schema directory and open the index.js file and add the following boilerplate code:

// Import External Dependancies const graphql = require('graphql') // Destructure GraphQL functions const { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLInt, GraphQLID, GraphQLList, GraphQLNonNull } = graphql // Import Controllers const carController = require('../controllers/carController') const ownerController = require('../controllers/ownerController') const serviceController = require('../controllers/serviceController') // Define Object Types const carType = new GraphQLObjectType({ name: 'Car', fields: () => ({}) }) const ownerType = new GraphQLObjectType({ name: 'Owner', fields: () => ({}) }) const serviceType = new GraphQLObjectType({ name: 'Service', fields: () => ({}) }) // Define Root Query const RootQuery = new GraphQLObjectType({ name: 'RootQueryType', fields: { car: {}, cars: {}, owner: {}, service: {} } }) // Define Mutations const Mutations = new GraphQLObjectType({ name: 'Mutations', fields: { addCar: { type: carType, args: {}, async resolve(args) { return '' } }, editCar: { type: carType, args: {}, async resolve(args) { return '' } }, deleteCar: { type: carType, args: {}, async resolve(args) { return '' } } } }) // Export the schema module.exports = new GraphQLSchema({ query: RootQuery, mutation: Mutations })

Let’s run through the above code:

We require the main GraphQL package and use JavaScript Destructuring to get the necessary GraphQL functions(GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLInt, GraphQLID, GraphQLList and GraphQLNonNull).

We import our three controllers (carController, ownerController and serviceController).

We declare the carType, ownerType and serviceTypeGraphQL Object Types, which are functions that accept an object as a parameter, with a name and a fields key.

These functions are used to define our GraphQL schema, similar to the Mongoose models defined earlier.

The fields can return a particular type, and methods that take arguments. Learn More about Object Types.

Then we declare the RootQuery which is also a GraphQL Object Type and is found at the top level of every GraphQL server. It represents all of the possible entry points into the GraphQL API. Learn More about root fields and resolvers.

We then declare our Mutations, which are used to change data. Although any query could be implemented to change data, operations that cause changes should be sent explicitly via a mutation. Learn More about Mutations.

Lastly we export the GraphQLSchema.

Now that we have our template setup we can start populating the Object Types, Root Query and Mutations.

Note that there are Mongoose to GraphQL schema generators available, but for the tutorial purposes we will manually create the schema.

Let’s update the carTypeObject Type as follows:

const carType = new GraphQLObjectType({ name: 'Car', fields: () => ({ _id: { type: GraphQLID }, title: { type: GraphQLString }, brand: { type: GraphQLString }, price: { type: GraphQLString }, age: { type: GraphQLInt }, owner_id: { type: GraphQLID }, owner: { type: ownerType, async resolve(parent, args) { return await ownerController.getSingleOwner({ id: parent.owner_id }) } }, services: { type: new GraphQLList(serviceType), async resolve(parent, args) { return await serviceController.getCarsServices({ id: parent._id }) } } }) })

Let’s dive deeper into the GraphQL functions, starting with the Scalars types in GraphQL:

GraphQL comes with a set of default scalar types out of the box:

  • Int: A signed 32‐bit integer. GraphQLInt
  • Float: A signed double-precision floating-point value. GraphQLFloat
  • String: A UTF‐8 character sequence. GraphQLString
  • Boolean: true or false. GraphQLBoolean
  • ID: The ID scalar type represents a unique identifier, often used to refetch an object or as the key for a cache. The ID type is serialised in the same way as a String; however, defining it as an ID signifies that it is not intended to be human‐readable. GraphQLID

The owner and service fields are where it gets interesting. These fields are not defined as Scalar types like the rest — instead, their type is referencing the ownerType and serviceType that we have created and are yet to populate.

Το δεύτερο επιχείρημα που περνάμε στα πεδία ownerκαι serviceείναι οι λειτουργίες επίλυσης.

Οι λειτουργίες ή οι μέθοδοι επίλυσης είναι συναρτήσεις που επιλύουν μια τιμή για έναν τύπο ή πεδίο σε ένα σχήμα

Τα εργαλεία επίλυσης μπορούν να είναι ασύγχρονα! Μπορούν να επιλύσουν τιμές από άλλο REST API, βάση δεδομένων, προσωρινή μνήμη, σταθερά κ.λπ.

Μπορείτε να σκεφτείτε κάθε πεδίο σε ένα ερώτημα GraphQL ως συνάρτηση ή μέθοδο του προηγούμενου τύπου που επιστρέφει τον επόμενο τύπο. Στην πραγματικότητα, έτσι λειτουργεί το GraphQL. Κάθε πεδίο σε κάθε τύπο υποστηρίζεται από μια συνάρτηση που ονομάζεται resolver και παρέχεται από τον προγραμματιστή του διακομιστή GraphQL. Όταν εκτελείται ένα πεδίο, ο αντίστοιχος αναλυτής καλείται να παράγει την επόμενη τιμή.

If a field produces a scalar value like a string or number, then the execution completes. However if a field produces an object value then the query will contain another selection of fields which apply to that object. This continues until scalar values are reached. GraphQL queries always end at scalar values.

In order to create the relationship between the different types we pass the _id and the owner_id values into the respective controller functions.

So essentially we are requesting the owner details along with the car details:

return await userController.getSingleOwner({ id: parent.owner_id })

and the details of all the services related to the car:

return await serviceController.getCarsServices({ id: parent._id })

To return a list or array from with GraphQL, we use the GraphQLList. Here is a great in depth tutorial about using arrays in GraphQL Schema, but it is really simple: whenever we need an array we will use the GraphQLList function.

Let’s update the ownerType and serviceType with the following code:

ownerType

const ownerType = new GraphQLObjectType({ name: 'Owner', fields: () => ({ _id: { type: GraphQLID }, firstName: { type: GraphQLString }, lastName: { type: GraphQLString }, email: { type: GraphQLString }, cars: { type: new GraphQLList(carType), async resolve(parent, args) { return await ownerController.getOwnersCars({ id: parent._id }) } } }) })

serviceType

const serviceType = new GraphQLObjectType({ name: 'Service', fields: () => ({ _id: { type: GraphQLID }, car_id: { type: GraphQLID }, name: { type: GraphQLString }, date: { type: GraphQLString }, car: { type: carType, async resolve(parent, args) { return await carController.getSingleCar({ id: parent.car_id }) } } }) })

The above two Object Types are very similar to the carType. You can notice a pattern between the different Object Types and their relationships.

We can now populate the RootQuery root with the following code:

const RootQuery = new GraphQLObjectType({ name: 'RootQueryType', fields: { car: { type: carType, args: { id: { type: GraphQLID } }, async resolve(parent, args) { return await carController.getSingleCar(args) } }, cars: { type: new GraphQLList(carType), async resolve(parent, args) { return await carController.getCars() } }, owner: { type: ownerType, args: { id: { type: GraphQLID } }, async resolve(parent, args) { return await ownerController.getSingleOwner(args) } }, service: { type: serviceType, args: { id: { type: GraphQLID } }, async resolve(parent, args) { return await serviceController.getSingleService(args) } } } })

There are no new concepts in the above code, but keep in mind that the RootQuery query is the entry point to all queries on the GraphQL API. So from the above we can see that we can run the following queries directly:

  • Get all the Cars
  • Get a single Car
  • Get a single Owner
  • Get a single Service

Let’s open the GraphiQL user interface and build some queries: //localhost:3000/graphiql.html

Queries are entered on the left, results are in the middle, and the documentation explorer is on the right.

The documentation explorer can be used to explore the entire graph down to Scalar level. This is very helpful when building queries.

The language used to build the queries resembles JSON. This cheat sheet is a great a reference.

Below demonstrates why GraphQL is so awesome:

In the above example, we are using the cars root query to display a list of all the cars, their owners, and their services.

We have one final topic to address, and that is mutations. Let’s update the mutations with the following code:

const Mutations = new GraphQLObjectType({ name: 'Mutations', fields: { addCar: { type: carType, args: { title: { type: new GraphQLNonNull(GraphQLString) }, brand: { type: new GraphQLNonNull(GraphQLString) }, price: { type: GraphQLString }, age: { type: GraphQLInt }, owner_id: { type: GraphQLID } }, async resolve(parent, args) { const data = await carController.addCar(args) return data } }, editCar: { type: carType, args: { id: { type: new GraphQLNonNull(GraphQLID) }, title: { type: new GraphQLNonNull(GraphQLString) }, brand: { type: new GraphQLNonNull(GraphQLString) }, price: { type: new GraphQLNonNull(GraphQLString) }, age: { type: new GraphQLNonNull(GraphQLInt) }, owner_id: { type: GraphQLID } }, async resolve(parent, args) { const data = await carController.updateCar(args) return data } }, deleteCar: { type: carType, args: { id: { type: new GraphQLNonNull(GraphQLID) } }, async resolve(parent, args) { const data = await carController.deleteCar(args) return data } } } })

As before, we declare our Object Type, specify the name and the fields.

A mutation consists of the the type, args and the async resolve function. The resolve function passes the args to the controller, which returns the result of the mutation.

Τώρα έχετε κωδικοποιήσει ένα πλήρως λειτουργικό API REST και ένα πλήρως λειτουργικό API GraphQL.

Δεν υπάρχουν κανόνες που να δηλώνουν ότι κάποιος πρέπει να χρησιμοποιεί αποκλειστικά REST ή αποκλειστικά GraphQL. Σε ορισμένα έργα, η καλύτερη λύση μπορεί να είναι ένας συνδυασμός και των δύο. Αυτό καθορίζεται πραγματικά βάσει έργου-προς-έργο.

Μπορείτε να κατεβάσετε τον πηγαίο κώδικα φόρμας Git εδώ.

Τι είναι το επόμενο;

Στο επόμενο σεμινάριο, θα καταναλώσουμε το GraphQL API με Vend.js frontend ως εφαρμογή μιας σελίδας!