6th Practical Class:
Persistent Backend!
Conect to server
- See 4th practical - MySQL connect
- You must update
.env
file in/backend
: - Fill in DB credentials (should be in your e-mail inbox)
- Switch
mocks
to false - Seed the DB -
backend/src/db/seed.sql
<- copy content of this file - Go to your PHPMyAdmin page (or other DB admin tool you are using), click on DB name
user_<username>
in left column -> sql (top line, 3th from left), paste content of file here ->go
- This should create
quack
anduser
tables and fill them with basic data
Mutation x Query differences
Query
serves to provide data, without mutating (changing them). REST
method would be GET
Mutation
servers for all data altering operations. REST
methods would be POST
, PUT
, DELETE
- This separation is
soft
- it is responsibility of programmer to enforce it. Eq, you can createquery
which adds new items. You definitelly should not! - It helps with easy API usage and maintenance. If it is ensured, that
query
never changes the data, you can run it any time, any number of times. You cancache
it, you can use bunch of other GraphQL tools with it - Always keep in mind, for every time you write the code, you will read it ~10 times! Optimize for redeability, it will save you headache down the road.
Mutatation
must have at least one argument (othervise it is pointles :D)Mutatation
does return data, same way asQueries
do. You can use this to pass operation result information, and / or new status of object
Detailed look at GraphQL server set-up
In backend/src/index.js
you have this:
What is going on here:
- Express (HTTP server) is initialized
x-powered-by
header is disabled- Basic CORS are set-up
- Check if we should use DB connect or no
- Apollo (GraphQL) server is initialized
Schema
(typeDefs),resolvers
,context
andplayground
are passed to Apollo- Apollo server is added to Express as a middleware
- Port setting
- Redirect from
/
to/graphql
to simplify work with server - Start of application - on what port, and what should be outputed on successfull start
Input x Type
Type
describes shape of data that will be provided by API - output shape
Input
describes shape of data that API will receive - input shape
Example Type
Example Input - full CUD
Validation
Why we should validate data on BE, when we are already doing validation on FE? Never thrust data sent over the internet! Client has full constroll over the app, he can sent what he wants. FE side validation are there for better user experience, BE validations are for security.
- Type validation is done by GraphQL, but only type validation. GraphQL does not check the values!
- You can and should do value validation in
resolvers
, before you run any custom logic. - Based on validation check result, you can either remove the value, ommit it from processing, or straight up
throw Error('Validation failed!')
. Idealy with bit more info then that! - There are ways how to automate common validations (email, phone number) across whole schema, by creating custom scalars. Note, you will not need this for this project :)
Resolver / controler
Eq the famous problem - where to put the logic?
Let's take Quack DB resolver as an example:
For a small and straight-forward functions, this code is OK, no need to optimize it. Let's have another example, this times BAD one:
As you can see, really long and ugly function. We can improve it by extracting logic to stand-alone functions:
fetchData
and prepareQuery
are named pretty generically, they need better names. But they could be extracted to separate files, and potentially reused in other resolvers or even other parts of app. Usually such files are called controller
, since they controll the business logic of app.
Ideal resolver could look like this:
How to work with DB on your project
- We are not using any ORM, that would normally be used with SQL DB. It is to simplify the project, ORM's caused quite a lot of pain in past years
- Store DB alteration commands in BE folder -
migrations
/seed
are common names. Idea is, that one person prepares commands which need to be run on BE to get the tables / indexes. Others then use these files to update their DB to current state - In tearms of geting / sending the data to DB, let's keep it simple - write down queries to your
resolvers
orcontrolers
- It is not ideal and you could run into some issues, but it should allow you to prototype pretty fast. Just keep in mind, that this needs to be updated, if you would like to use this project as base for production-ready app