React-Apollo and Recompose Live: Part 1

FP with Recompose and React-Apollo

Notice: I don’t know if I’m a programmer or not. I’ve never worked with anyone, never discussed programming or javascript face to face with anyone. That is reflected in the style of this post. If it looks like it gets basic at certain points it’s because I’m telling you what I see, as I see it as Kublai Kahn would have wanted. More in Part 2 & Part 3.

Kublia Kahn from Netflix’s Marco Polo, Episode 1. Credits: SpringfieldSpringfield , imgflip.com

I think its important because I believe there are a lot developers learning in the dark like I am. I ask stupid questions all the time. Most of the time its purposeful just so I hear the answer and see what nuggets are in it. For example, I would love to know what the other techniques are for running regex in GraphQL. This isn’t clean, it’s real. Let’s learn.

WHY DID I DO THIS?

I love functional style programming and GraphQL. I had to see how the demo code from this article using Recompose, React-Apollo and Apollo-Client worked so I worked backwards from the example to add a backend to it. I mostly did to see if I could I ended up doing 3 different ways, just to see how its done. Below are the steps I took to make a version that strictly adheres to the Apollo Version. In Part 2, I will show you how I did it with Graphcool.

TLDR

The Live Demo: Deployed!

The Github Repo: Repository!

Meta

Only way I know how to run a search with GraphQL is to use MongoDB on the backend and use the RegEx operator there. So I had to be put up a quick sandbox on [mLab] (www.mlab.com)

For a functioning GraphQL endpoint to test against that I could connect to MongoDB, I went with Apollo’s awesome Launchpad. My first run at this was with GraphCool but you don’t have control of the resolvers as I understand it so I could not really get at the Regex thing. They do have an Algolia integration that seems worth checking out.

For fake data, I used Mockaroo because their regex functionality is super cool and makes it easy to create nested objects which the demo schema required.

Deploying the demo was pretty easy with create-react-app-now. It was my first time using it so it wasn’t so clear but if you just follow the instructions, it apparently works!

Setup MongoDB on Mlab.com

Go to mLab and get create a sandbox then create a user for the database your created. Note the user’s login and password somewhere for later. IMPORTANT: Your mLab login and your database user login are two different things.

Also note your connection string. It will look something like this.

connection string: mongodb://<dbuser>:<dbuserpassword>@ds0305555.mlab .com:305555/books-recompose

I used mongoimport to import a json array so don’t forget the — jsonArray flag on your mongoimport command if your json is in an array. This is assuming you have MongoDB installed. Install it if you need to. Your mLab mongoimport command should look something like this.

mongoimport -h ds0305555.mlab.com:305555 -d books-recompose -c books -u <dbuser> -p <dbuserpasswrod> --file ./books.json --jsonArray

Setup Your GraphQL Endpoint

Get the Apollo Launchpad MongoDB example, click fork at the top of the screen.

Your will be prompted to change the sandbox secrets that that endpoint relies on. Find your mLab connection string and add as the value for the MONGODB_URL key.

This is my Launchpad for this project: https://launchpad.graphql.com/k7mmlwwl7

Change the Launchpad schema to conform to demo example. This it what it looks like:

export const schema = buildSchema(`
type Book {
id: ID!
isbn: String!
title: String!
author: Author!
}
type Author {
id: ID!
name: String!
}
type Query {
books(keyword:String): [Book]
book(id: ID!): Book
}`);
export const rootValue = {
books: async (args, context) => {
let findParams = {}
if (args.keyword) {
findParams.title = new RegExp(args.keyword, 'i')
}
return context.mongo.collection('books').find(findParams).map(fromMongo).toArray();
},
book: async ({id, title}, context) => {
const result = await context.mongo
.collection('books')
.findOne({_id: new ObjectID(id) })
return fromMongo(result)
},
}

The RegEx Resolver

The point of this exercise was to access the $regex operator from MongoDB because GraphQL doesn’t support RegEx. For example, if you don’t need regex for searching, you could have just had the array in the file like in the MongoDB Launchpad Example. I actually tried to do this with Lunrjs just to see if I could. That was fun, but my uptake was not so quick. Then I tried ZangoDB, which emulates the MongoDB methods in the browser. The docs didn’t mention regex emulation so I let it go. I will be going back to ZangoDB, though. It looks really promising. After all that failing and learning, I went back to something I already new I could do. The mental drag for me was thinking about where I was going to host the GraphQL endpoint which is why I tried Graphcool first. Then it hit me that Launchpad would allow me to connect to MongoDB on mLab and get the regex function I was hunting down for search. Lest you think I’m down on Graphcool, I actually have this all done on there so look out for Part 2. My original goal was to make the app look like the demo code and I could not do that from there.

The Schema and Resolver

// Query Schema
...
type Query {
books(keyword:String): [Book]
book(id: ID!): Book
}`
);
// Query Books/Resolver
books: async (args, context) => {
let findParams = {}
if (args.keyword) {
findParams.title = new RegExp(args.keyword, 'i')
}

return context.mongo.collection('books')
.find(findParams)
.map(fromMongo)
.toArray();
},

To make this work, you add an optional/non-required variable argument to the books query. Then in the resolver for the books query field, you instantiate an object to hold any params that might have been passed in. On the next line, you use an ‘if’ statement that reads, if args.keyword is true, do the next line, if its false, just go ahead and resolve with no parameters. If there is a keyword passed through add it to the findParams object as the value for the title key for our books as a regex operation. You then pass it to the call to MongoDB which will run a regex for our keyword on the title field. If you look at the app you will notice it populates with initial data. But then changes that data with a query. So the optional parameter lets us run an initial query with no params then use the same resolver to pass parameters if they are called. Like any good programmer, I did not make this up. Samer Buna taught me that for $5 in his book Learning Relay and GraphQL.

Once we get test data into our database we will be able to run some queries against the schema to test it. Let’s test the query which has driven me to right an entire blog post once we get the data imported later.

// query
query BookQuery($keyword:String!){
books(keyword:$keyword){
title
author {
id
name
}
}
}
// query variables
{
"keyword": "bamity"
}

Generating Sample Data

Now that we’ve have the code for the server, we just have to set up some data and set up the client. I used Mockaroo to generate fake data that conforms to the demo example. Mockaroo has great regex capabilites that let me get the nested JSON object format the demo suggested. Of course, with GraphQL the data could have looked like anything and been returned however we wanted so I made an assumption that the objects looked like this.

Import the sample data into your mLab sandbox MongoDB instance.

Get the generated Launchpad Endpoint at the bottom of you Launchpad screen. The url for this project is :https://k7mmlwwl7.lp.gql.zone/graphql.

https://k7mmlwwl7.lp.gql.zone/graphql

We can now run the query above to see if it works. Paste the query into the top part of the graphiql side of Launchpad and the query variables into the bottom. Now click ▶ and watch it go! This is RegEx so you can cut the word in half and the query will return all results with the partial word in the title field.

Building The App

Create a starter project with create-react-app-now, instead of create-react-app to ease deployment later on. Just follow the repo directions. Its that simple.

Add the project dependencies we are testing.

npm install -S apollo-client react-apollo recompose react-router-dom

The demo assumes there is an App.js file calling BookSearch.jsx so we need to import it to App.js. This is what it should look like.

Drop in refactored demo code from the article. The refactor in the article not expected to run so I had to clean it up just a bit. This is what it looks like.

Put some minimum css in it so it doesn’t hurt the eyes (even if you don’t know css). Get it here if you need it.

Deploy with Now-CLI.

npm run deploy

If you are familiar with Now, you might notice that the console messages for create-react-app-now are not as clean. Open up your Now account in the browser to see if its deployed before running it again.

The code works. The list renders in a clean functional code style. Searching re-renders the list with the same code.

Credit: https://gyazo.com

Thanks to Apollo for putting out great material so that rookie programmers a chance to learn. Also, thank you to Sashko Stubailo for reviewing this post, my first legitimate attempt at a programming post. I noticed after he offered to help that the pinned tweet on his profile is offering mentorship.

Here he was offering it without being asked. I’ve been actively looking for a mentor so, Uh, Sacha….?

If you think I have a clue with this javascript thing, I’m open to working in Paris or anywhere in Europe. Thanks for listening.

Questions

Explaining this in writing helps me learn and remember. So does you asking me questions. Any questions?