GraphQL has mutations, Postgres has INSERT; GraphQL has queries, Postgres has SELECT's; etc., etc.. I haven't found an example showing how you could use both in a project, for example passing all the queries from front end (React, Relay) in GraphQL, but to a actually store the data in Postgres.
Does anyone know what Facebook is using as DB and how it's connected with GraphQL?
Is the only option of storing data in Postgres right now to build custom "adapters" that take the GraphQL query and convert it into SQL?
GraphQL is database agnostic, so you can use whatever you normally use to interact with the database, and use the query or mutation's resolve method to call a function you've defined that will get/add something to the database.
Without Relay
Here is an example of a mutation using the promise-based Knex SQL query builder, first without Relay to get a feel for the concept. I'm going to assume that you have created a userType in your GraphQL schema that has three fields: id, username, and created: all required, and that you have a getUser function already defined which queries the database and returns a user object. In the database I also have a password column, but since I don't want that queried I leave it out of my userType.
// db.js
// take a user object and use knex to add it to the database, then return the newly
// created user from the db.
const addUser = (user) => (
knex('users')
.returning('id') // returns [id]
.insert({
username: user.username,
password: yourPasswordHashFunction(user.password),
created: Math.floor(Date.now() / 1000), // Unix time in seconds
})
.then((id) => (getUser(id[0])))
.catch((error) => (
console.log(error)
))
);
// schema.js
// the resolve function receives the query inputs as args, then you can call
// your addUser function using them
const mutationType = new GraphQLObjectType({
name: 'Mutation',
description: 'Functions to add things to the database.',
fields: () => ({
addUser: {
type: userType,
args: {
username: {
type: new GraphQLNonNull(GraphQLString),
},
password: {
type: new GraphQLNonNull(GraphQLString),
},
},
resolve: (_, args) => (
addUser({
username: args.username,
password: args.password,
})
),
},
}),
});
Since Postgres creates the id for me and I calculate the created timestamp, I don't need them in my mutation query.
The Relay Way
Using the helpers in graphql-relay and sticking pretty close to the Relay Starter Kit helped me, because it was a lot to take in all at once. Relay requires you to set up your schema in a specific way so that it can work properly, but the idea is the same: use your functions to fetch from or add to the database in the resolve methods.
One important caveat is that the Relay way expects that the object returned from getUser is an instance of a class User, so you'll have to modify getUser to accommodate that.
The final example using Relay (fromGlobalId, globalIdField, mutationWithClientMutationId, and nodeDefinitions are all from graphql-relay):
/**
* We get the node interface and field from the Relay library.
*
* The first method defines the way we resolve an ID to its object.
* The second defines the way we resolve an object to its GraphQL type.
*
* All your types will implement this nodeInterface
*/
const { nodeInterface, nodeField } = nodeDefinitions(
(globalId) => {
const { type, id } = fromGlobalId(globalId);
if (type === 'User') {
return getUser(id);
}
return null;
},
(obj) => {
if (obj instanceof User) {
return userType;
}
return null;
}
);
// a globalId is just a base64 encoding of the database id and the type
const userType = new GraphQLObjectType({
name: 'User',
description: 'A user.',
fields: () => ({
id: globalIdField('User'),
username: {
type: new GraphQLNonNull(GraphQLString),
description: 'The username the user has selected.',
},
created: {
type: GraphQLInt,
description: 'The Unix timestamp in seconds of when the user was created.',
},
}),
interfaces: [nodeInterface],
});
// The "payload" is the data that will be returned from the mutation
const userMutation = mutationWithClientMutationId({
name: 'AddUser',
inputFields: {
username: {
type: GraphQLString,
},
password: {
type: new GraphQLNonNull(GraphQLString),
},
},
outputFields: {
user: {
type: userType,
resolve: (payload) => getUser(payload.userId),
},
},
mutateAndGetPayload: ({ username, password }) =>
addUser(
{ username, password }
).then((user) => ({ userId: user.id })), // passed to resolve in outputFields
});
const mutationType = new GraphQLObjectType({
name: 'Mutation',
description: 'Functions to add things to the database.',
fields: () => ({
addUser: userMutation,
}),
});
const queryType = new GraphQLObjectType({
name: 'Query',
fields: () => ({
node: nodeField,
user: {
type: userType,
args: {
id: {
description: 'ID number of the user.',
type: new GraphQLNonNull(GraphQLID),
},
},
resolve: (root, args) => getUser(args.id),
},
}),
});
We address this problem in Join Monster, a library we recently open-sourced to automatically translate GraphQL queries to SQL based on your schema definitions.
This GraphQL Starter Kit can be used for experimenting with GraphQL.js and PostgreSQL:
https://github.com/kriasoft/graphql-starter-kit - Node.js, GraphQL.js, PostgreSQL, Babel, Flow
(disclaimer: I'm the author)
Have a look at graphql-sequelize for how to work with Postgres.
For mutations (create/update/delete) you can look at the examples in the relay repo for instance.
Postgraphile https://www.graphile.org/postgraphile/ is Open Source
Rapidly build highly customisable, lightning-fast GraphQL APIs
PostGraphile is an open-source tool to help you rapidly design and
serve a high-performance, secure, client-facing GraphQL API backed
primarily by your PostgreSQL database. Delight your customers with
incredible performance whilst maintaining full control over your data
and your database. Use our powerful plugin system to customise every
facet of your GraphQL API to your liking.
You can use an ORM like sequelize if you're using Javascript or Typeorm if you're using Typescript
Probably FB using mongodb or nosql in backend. I've recently read a blog entry which explain how to connect to mongodb. Basically, you need to build a graph model to match the data you already have in your DB. Then write resolve, reject function to tell GQL how to behave when posting a query request.
See https://www.compose.io/articles/using-graphql-with-mongodb/
Have a look at SequelizeJS which is a promise based ORM that can work with a number of dialects; PostgreSQL, MySQL, SQLite and MSSQL
The below code is pulled right from its example
const Sequelize = require('sequelize');
const sequelize = new Sequelize('database', 'username', 'password', {
host: 'localhost',
dialect: 'mysql'|'sqlite'|'postgres'|'mssql',
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
},
// SQLite only
storage: 'path/to/database.sqlite',
// http://docs.sequelizejs.com/manual/tutorial/querying.html#operators
operatorsAliases: false
});
const User = sequelize.define('user', {
username: Sequelize.STRING,
birthday: Sequelize.DATE
});
sequelize.sync()
.then(() => User.create({
username: 'janedoe',
birthday: new Date(1980, 6, 20)
}))
.then(jane => {
console.log(jane.toJSON());
});
I upgrade sails to 1.0, I resolved all the other errors but not able to resolved database connection issue,
It will be really helpful if anyone can reply on this.
module.exports.datastores = {
localDiskDb: {
adapter: 'sails-disk'
},
postgreSql: {
adapter: 'sails-postgresql',
// url: 'postgresql://admin:root#localhost:5432/testdb',
// ssl: true,
host: 'localhost',
user: 'admin',
password: 'root',
database: 'testdb'
},
};
Important Error Logs
I was using password which contains %
after changing the password sails successfully connected to postgresql.
It mostly looks like bug with Sails v1 or Sails-pgsql adapter.
I have a sails app working against a legacy database (MySQL) and I would like to perform integration tests. I am using fixtures to load data into a separate test database using barrels. When I run my tests I get an error:
[Error (E_UNKNOWN) Encountered an unexpected error] Details: Error: ER_PARSE_ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')' at line 1
Interview.js api/models/Interview.js:
module.exports = {
tableName: 'interview',
attributes: {
interviewId: {
type: 'integer',
columnName: 'interview_id',
primaryKey: true,
unique: true
},
...
}
};
interview.json tests/fixtures:
[
{
"interviewId": 1,
"title": "some string",
"createdDate": "2015-11-23T09:09:03.000Z",
"lastModified": "2015-11-23T09:09:03.000Z"
},
{...}
]
test environment config/env/test.js :
models: {
connection: 'test',
migrate: 'drop',
autoPK: false,
autoCreatedAt: false,
autoUpdatedAt: false
}
The problem seems to lie in defining a primary key in the schema rather than letting sails create one automatically. If I remove the interviewId field from the model and set autoPK: true it works. But this does not accurately represent my data structure.
App info:
sails#0.11.2 sails-mysql#0.11.2 waterline#0.10.27 barrels#1.6.2 node v0.12.7
Many thanks,
Andy
I am quite new to Node. I came across Sails.js. I think it is based on WebSocket, which seems to be really good for building real-time applications. I would like to know that whether Sails can be used to implement REST architecture as it uses WebSocket? And if yes, how?
Yes it can. Sails JS allows you to easily build a RESTful API, essentially with no effort to get started. Also, websockets (through socket.io) are integrated by default into the view and api.
To create a fully RESTful app from the ground up, it actually requires no JS. Try:
sails new testapp
cd testapp
sails generate model user
sails generate controller user
cd <main root>
sails lift
The CRUD (Create, Read, Update, Delete) actions are already created for you. No code!
You can create a user in your browser by doing the following:
HTTP POST (using a tool like PostMan) to http://:1337/user/create
{
"firstName": "Bob",
"lastName": "Jones"
}
Next, do a GET to see the new user:
HTTP GET http://:1337/user/
FYI - Sails JS uses a default disk based database to get you going
Done.
sails new testapp
cd testapp
sails generate api apiName
controller
create: function (req, res) {
var payload = {
name:req.body.name,
price:req.body.price,
category:req.body.category,
author:req.body.author,
description:req.body.description
};
Book.create(payload).exec(function(err){
if(err){
res.status(500).json({'error':'something is not right'})
}else{
res.status(200).json({'success':true, 'result':payload, 'message':'Book Created success'})
}
});
},
readone: async function (req, res) {
var id = req.params.id;
var fff = await Book.find(id);
if(fff.length == 0){
res.status(500).json({'error':'No record found from this ID'})
}else{
res.status(200).json({'success':true, 'result':fff, 'message':'Record found'})
}
},
model
attributes: {
id: { type: 'number', autoIncrement: true },
name: { type: 'string', required: true, },
price: { type: 'number', required: true, },
category: { type: 'string', required: true, },
author: { type: 'string' },
description: { type: 'string' },
},
routes
'post /newbook': 'BookController.create',
'get /book/:id': 'BookController.readone',
i'm using
node 0.4.11
express 2.4.6
mongodb 1.8.3
mongoose 2.1.2
connect-mongodb 1.0.0
and trying to implement replica sets with authentication.
i want to store different kind of application-data in the DB
i want to store express' session-data in the DB
a "normal" connection with mongoose is working:
mongo.connectSet('mongodb://user:secret#host:27017/test,
mongodb://host:27018,
mongodb://host:27019,
mongodb://host:27020', function (err) {
if (err) {
console.log("could not connect to DB: " + err);
}
});
but how can get the session management to work?!
app.use(express.session({
cookie: {maxAge: 60000 * 20}, // 20 minutes
secret: 'foo',
store: new MongoStore({
host: 'host',
port: [27017, 27018, 27019, 27020],
dbname: 'test',
rs_name: 'rstest',
username: 'user',
password: 'secret'
})
}));
this is not working :(
is this actually possible? or should i use a different mongodb for storing the session data?
Are you sure you are using connect-mongodb and not connect-mongo?
I don't see where those parameters can be used with connect-mongodb.
Don't pass in the connection settings. connect-mongodb can take a direct db variable instead, which is an instance of mongodb.Db.
That means you can use the same connection that mongoose uses, instead of having connect-mongodb create a new connection just for sessions.
For mongoose, the mongodb.Db instance can be found at mongoose.connection.db.
So using your code as an example (assuming mongo is your mongoose object):
app.use(express.session({
cookie: {maxAge: 60000 * 20}, // 20 minutes
secret: 'foo',
store: new MongoStore({db: mongo.connection.db})
}));