I would like to write some documentation about adding support for mutations that connects to a local psql db.
Some of my concerns are:
Another big concern is that I'm not sure if it's optimal to use RSK for mutations, and instead use nodejs-api-starter. The drawback here is the increased cost and complexity of an interactive(CRUD) - react-starter-kit - application, that don't need third-party data.
The changes that needs to be made
At first, you should design some kind of security/access rights/ACL.
The best places to put mutation queries(separate folder or inside existing 'queries')
I have separate directory src/data/mutations, GraphQL follows GraphQL Relay Specification in almost all.
How to configure the store and reducers correctly
You do not need this with Apollo, but of course, there are special cases where using Redux with Apollo together have nice side effects :-)
In Apollo just return type which you mutated. If you have proper global identifier, everything should work like a charm :-)
What imports I should use if I want to upload files, and generally when uploading data
Always depends… One possible solution and most of the others :smiling_imp:
At first, you should design some kind of security/access rights/ACL.
I'd really like to know how you've handled this part, have some code you could share? :)
I personally prefer to write graphql schemas using apollographql/graphql-tools, I find that the code is a lot easier to read/write
If you wanted to go the graphql-tools route, you could do something like this:
Create a schema.js file in the data folder, this will be the file that combines all of your sub-schemas together. Let's also add a query, a mutation and a type.
src/data/schema.js
import { merge } from 'lodash';
import { makeExecutableSchema, addErrorLoggingToSchema } from 'graphql-tools';
import {
schema as DatabaseSchema,
resolvers as DatabaseResolvers,
mutations as DatabaseMutations,
queries as DatabaseQueries
} from './types/Database/schema';
const logger = { log: (e) => console.error(e.stack) };
const RootQuery = [`
type RootQuery {
${DatabaseQueries}
}
`];
const Mutation = [`
type Mutation {
${DatabaseMutations}
}
`];
const SchemaDefinition =[`
schema {
query: RootQuery
mutation: Mutation
}
`];
// Merge all of the resolver objects together
// Put schema together into one array of schema strings
const resolvers = merge(
DatabaseResolvers
);
const schema = [
...SchemaDefinition,
...RootQuery,
...DatabaseSchema,
...Mutation
];
export default makeExecutableSchema({
typeDefs: schema,
resolvers,
logger,
});
Then create a src/data/types folder. I have my apps organized by making a folder for each "type of type" i.e. database has users, Stripe api has users etc., and each type folder gets it's own schema.js file. So for example, make a folder called Database and create a file in Database called schema.js
src/data/types/Database/schema.js will combine all Database queries/mutations/types, and this will get combined into the overall schema in src/data/schema.js. You will likely have many sub-schema files which can be added to src/data/schema.js in the same way as Database.
Create src/data/types/Database/schema.js
src/data/types/Database/schema.js
import { merge } from 'lodash';
/*** Queries ***/
import { schema as GetAllUsers, queries as GetAllUsersQueries, resolvers as GetAllUsersResolver } from './users/GetAllUsers';
/*** Mutations ***/
import { mutation as CreateUser, resolvers as CreateUserResolver } from './users/CreateUser';
// Merge all of the resolver objects together
// Put schema together into one array of schema strings
export const resolvers = merge(
GetAllUsersResolver,
CreateUserResolver
);
export const schema = [
...GetAllUsers,
];
export const mutations = [
...CreateUser,
]
export const queries = [
...GetAllUsersQueries,
]
Finally, create src/data/types/Database/users folder and create a src/data/types/Database/users/GetAllUsers.js file. This is where you define a database user type, schema and resolver
src/data/types/Database/users/GetAllUsers.js
import { User, UserClaim, UserLogin, UserProfile } from '../../../models';
export const schema = [`
type DatabaseUser {
id: String
email: String
emailConfirmed: Boolean
logins: [DatabaseUserLogin]
claims: [DatabaseUserClaim]
profile: DatabaseUserProfile
updatedAt: String
createdAt: String
}
type DatabaseUserLogin {
name: String
key: String
createdAt: String
updatedAt: String
userId: String
}
type DatabaseUserClaim {
id: Int
type: String
value: String
createdAt: String
updatedAt: String
userId: String
}
type DatabaseUserProfile {
userId: String
displayName: String
picture: String
gender: String
location: String
website: String
createdAt: String
updatedAt: String
}
`];
export const queries = [`
databaseGetAllUsers: [DatabaseUser]
databaseGetUser(id: String!): DatabaseUser
`];
export const resolvers = {
RootQuery: {
databaseGetAllUsers: (parent, args) => {
return User.findAll({
include: [
{ model: UserLogin, as: 'logins' },
{ model: UserClaim, as: 'claims' },
{ model: UserProfile, as: 'profile' },
],
}).then((users) => {
//console.log(JSON.stringify(users, null, 4));
return users.map(user => {
return user;
});
}).catch((err) => {
throw err;
})
},
databaseGetUser: (parent, args) => {
return User.findOne({
where: {id: args.id},
include: [
{ model: UserLogin, as: 'logins' },
{ model: UserClaim, as: 'claims' },
{ model: UserProfile, as: 'profile' },
],
}).then((user) => {
//console.log(JSON.stringify(user, null, 4));
return user;
}).catch((err) => {
throw err;
})
},
},
};
You can add mutations in a very similar fashion to queries.
src/data/types/Database/users/CreateUser.js
import { User } from '../../../models';
export const mutation = [`
databaseCreateUser(
name: String!
): DatabaseUser
`];
export const resolvers = {
Mutation: {
databaseCreateUser: (parent, args) => {
return User.create({...args}).then((user) => {
//console.log(user.dataValues);
return user.dataValues;
}).catch((err) => {
//console.log(JSON.stringify(err, null, 2));
throw err.name + ": " + err.parent.code;
})
},
},
};
Welp... this post turned into something really long. I hope that helps. I could flesh this out better in a code recipe if there is demand :P
@tim-soft Wow very powerful writing. Not for GH I believe. This should be at Wiki. Can you?
Most definitely, I've had a lot of fun with your apollo branch :)
Maybe the starter schema in the apollo branch should be done this way? A lot easier imo
@tim-soft I'm in phase searching you, wish write private mail :-) I really need someone who can help me :smiling_imp:
My branches are really minimal in needs — but I hope helping a lot..
Wow, nice replies! I greatly appreciate it.
I'm sorry but I forgot to write that I'm trying to do this on the feature/apollo branch, I don't know if that makes any difference to your suggestions though.
So far I've written a poorly edited guide to setting up a secure PSQL DB, with a user that has limited permissions. I can share it somewhere if you can't wait to look at it. :slightly_smiling_face:
I'm still filling it out and am trying to make a complete guide 'from cloning RSK, to adding data to the table'
The changes I think has to happen (given my mutation is called "createTest"):
Assuming you have PSQL running and set up locally:
/src/configAdd the following inside the /src folder:
/data/types/data called mutations/models/data/models/index/data/schemaP.S I've not been able to make a mutation yet, but I'm able to see it on the /graphql endpoint.
Am I missing anything or on the completely wrong track? Again, thank you for the great replies!
I'm working on a guide for the wiki for doing what I mentioned earlier. Nuke your queries folder and give this a try ;)
I got it to work with your code, even with a test mutation and everything! Really nice. I only encountered one problem: when importing the schemas in /data/database/schema.js; I got some weird errors until I figured I had to use [... DatabaseSchema, ...TestSchema] to merge the schemes properly. Let me know if I can help with anything and thanks for the great examples. :)
I'm soon done with the first iteration of a small guide on how to connect to PostgreSQL and make PostgreSQL and Docker talk.
The only 'problem' I've thought about but not done anything, is the manual change of the URI if you want to run the app in dev mode outside of Docker.
@GideonFlynn very nice small guide :+1: :-)
The only 'problem' I've thought about but not done anything, is the manual change of the URI if you want to run the app in dev mode outside of Docker.
You can pass any string via environment variables. You can pass environment vars into docker too.. then just read them from process.env
Most helpful comment
I'd really like to know how you've handled this part, have some code you could share? :)
I personally prefer to write graphql schemas using apollographql/graphql-tools, I find that the code is a lot easier to read/write
If you wanted to go the graphql-tools route, you could do something like this:
Create a schema.js file in the data folder, this will be the file that combines all of your sub-schemas together. Let's also add a query, a mutation and a type.
src/data/schema.js
Then create a
src/data/typesfolder. I have my apps organized by making a folder for each "type of type" i.e. database has users, Stripe api has users etc., and each type folder gets it's ownschema.jsfile. So for example, make a folder calledDatabaseand create a file inDatabasecalledschema.jssrc/data/types/Database/schema.jswill combine all Database queries/mutations/types, and this will get combined into the overall schema insrc/data/schema.js. You will likely have many sub-schema files which can be added tosrc/data/schema.jsin the same way asDatabase.Create
src/data/types/Database/schema.jssrc/data/types/Database/schema.js
Finally, create
src/data/types/Database/usersfolder and create asrc/data/types/Database/users/GetAllUsers.jsfile. This is where you define a database user type, schema and resolversrc/data/types/Database/users/GetAllUsers.js
You can add mutations in a very similar fashion to queries.
src/data/types/Database/users/CreateUser.js
Welp... this post turned into something really long. I hope that helps. I could flesh this out better in a code recipe if there is demand :P