Rails graphql

Rails graphql DEFAULT

TL;DR; GraphQL is a good way of making your API more flexible and less resource consuming. But if you think that type-definition is cumbersome then read on. With the modules we provide you'll be able to expose fully functional resources with one line of code.

For those who haven't followed the GraphQL trend launched by Facebook, it's a fancy way of mixing API and SQL concepts together.

Instead of making calls to a properly structured endpoint with parameters like with REST APIs, GraphQL makes you build syntactic queries that you send to one endpoint.

The benefit of GraphQL? A properly defined standard for:

  • Making multiple queries as once
  • Forcing consumers to select the fields they need
  • Fetching related resources as part of parent resources
  • Paginating resources and sub-resources (using relay-style pagination)
  • Strongly-typing the resources you expose
  • Documenting your API without the immediate need for a separate documentation website

Couldn't a REST API do the above? Of course it could. But GraphQL has defined a standard for all these and many clients are already out there providing out of the box functionalities for interacting with GraphQL APIs. So...why not give it a try?

If you need more convincing you can read GitHub's blog article explaining why they switched.

When it comes to implementing a GraphQL server in Rails, one can use the excellent GraphQL Ruby gem.

The gem provides all the foundations for building your API. But the implementation is still very much manual, with lots of boilerplate code to provide.

In this article I will guide you through the steps of bootstrapping GraphQL Ruby then show you how - with a bit of introspection - you can easily expose your resources the Rails Way™ (= with one line of code).

First steps with graphql-ruby

Let's dive into graphql-ruby and see how we can go from zero to first query.

Installing graphql-ruby

First add the graphql gem to your Gemfile:

Then run the install generator:

The generator will create the GraphQL controller, setup the base types and update your routes.

That's it for the install part. Now let's see how we can expose resources to query.

Defining and exposing models

The first important file to look at is the Types::QueryType file. This class defines all the attributes which can be queried on your GraphQL API.

For the purpose of demonstrating how records get exposed, let's generate a User and a Book model.

We'll expose these two classes for querying on our GraphQL API. To do so we need to define their type.

We'll start by defining a base type for common record attributes. These kind of base classes can help keep your type classes more focused.

Then let's define GraphQL types for our models.

This is the User type:

This is the Book type. You'll notice that the user field reuses the User type.

Now that we have defined our types we need to plug them to the GraphQL Query API. This plumbing happens in the Types::QueryType class.

Here is the generated Types::QueryType class that we have expanded a bit to expose our collections. We use connection_type instead of arrays on the Book and User types so as to automatically benefit from relay-style pagination.

Let's see how we can use our API now.

Querying the GraphQL API

The easiest way to query your GraphQL API is to use GraphiQL.

Good news though, the GraphQL gem generator automatically adds the graphiql-rails gem to your gemfile. After running bundle install you should be able to access GraphiQL on http://localhost:3000/graphiql

You might encounter a precompilation error. In that case update your manifest.js and add the GraphiQL assets.

If you prefer, you can also install GraphiQL as a standalone app. See this link for more info.

When you open GraphiQL, the first thing you should look at is the docs section. You'll notice that all your models and fields are properly documented there. That's neat.

Let's create some test records via the Rails console:

Cool. Now we can perform our query.

Note how GraphQL allows us to perform multiple queries at once. That's really sweet.

Adding filtering attributes to your collections

It would be nice to have filters on our collections. The gem allows us to do that via field block definitions.

Here is a concrete example of adding a filter on page size.

Now you can easily filter on book size.

Nice!

But I'm used to Rails where everything is inferred out of the box. Right now it looks quite cumbersome to define all these collections and filters. Isn't there a way to automatically generate those?

Of course there is. Time to use GraphQL custom resolvers with a bit of introspection!

Automatically defining resources and filters

In order to automatically build resources and their corresponding filters we'll need three things:

  • A GraphQL helper to expose Active Record resources
  • A custom resolver authorizing and querying our collections
  • An Active Record helper to evaluate the query filters received from GraphQL.

The modules below are configured to use Pundit - if present - to scope access to records. Pundit is really just given as an example - any scoping framework would work, even custom policy classes.

Active Record query helpers

Let's start with the Active Record helper.

Add the following concern to your application. This concern allows collections to be filtered using underscore notation (e.g. created_at_gte for created_at >=) and sorting using dot notation (e.g. created_at.desc).

Use this concern in your ApplicationRecord base class.

Great! Now you can filter and sort records this way:

The concern also defines a default graphql_scope, which is used by our resolvers. This scope can be overridden on each model to define API-specific eager loading strategies.

Here is an example with our book model:

GraphQL custom resolvers for collection and find queries

Now let's add a custom resolver to dynamically support our collections and corresponding filters.

The resolver looks at all the fields defined on the model type and automatically generate filters for fields which are database queriable.

Let's also add a custom resolver to support fetching model by unique attribute.

Any field your define as ID on your model types will be exposed as a primary key for record fetching purpose.

In both resolvers I've made Pundit optional. But I strongly recommend using it or any similar framework. You should read the comments above each pundit_ method in the resolver and adapt based on your needs.

For authorization purpose, you can inject a current_user attribute inside the GraphQL context by modifying your GraphqlController. Here is an example:

GraphQL base object to define resources and has_many

We have custom resolvers to handle the GraphQL query logic and model-level helpers to translate these into database-compatible filters. The last missing piece is a helper allowing us to declare our GraphQL resources.

To do this, add the following helper methods to your Types::BaseObject class.

The above provides two helpers:

  • resource: a helper to be used inside Types::QueryType to expose an Active Record model for collection querying and record fetching.
  • has_many: a way to define sub-collections on a type.

You can now rewrite your Types::QueryType class the following way:

Also let's add a has_many books on our User model and type:

Querying our newly implemented resources

We're ready. Let's see how this works now.

As you can see on the right-hand side, all our collection filters are properly generated. We can also fetch records individually by ID field (id or any other ID field on the type). Finally, we can fetch sub-resources on parent records, such as user books.

Wrapping up

A bit of metaprogramming makes the whole GraphQL-Rails experience way easier than it was originally advertised. Now all we need to do is define model types and declare resources in our Types::QueryType.

But there is more we can do. In the next episodes we'll see how to do similar things for mutations (create/update/delete) and subscriptions (via Pusher as a specific example).

About us

Keypup is on a mission to help developers and tech leads work better with each others on development projects. Our platform automatically centralizes, prioritizes and assigns people and actions on issues and pull requests to optimize your development flow.

Don't get lost because you have to juggle with twenty pull requests across five development projects. We'll clean and organize that for you to ensure a smooth landing.

---

Code snippets hosted with ❤ by GitHub

Arnaud Lachaume

Chieft Technology Officer at Keypup

Sours: https://www.keypup.io/blog/graphql-the-rails-way-part-1-exposing-your-resources-for-querying

Rails, GraphQL, and TypeScript: a monolith worth trying

I'm a software engineer living and learning in San Francisco, California.

Published

Ruby on Rails has been a powerful framework for building web apps for well over a decade. In recent years, Rails has expanded its capabilities to support modern frontend technologies with barely any configuration. In doing some proof-of-concept work for an app idea, I built a stack I’m quite happy with.

  • Rails serves a GraphQL API.
  • Rails also serves a webpack-based frontend, and compiles it for us.
  • Types are synchronized between GraphQL and TypeScript, so I can never misuse query results.

All of this was achieved with very little configuration thanks to Rails’ excellent defaults.

If you’d like to see a working example, I’ve built a proof-of-concept repository with these features fully set up. The commits mimic the steps in this post.


Starting with Rails

I began with a fresh Rails app, but it’s just as easy to set this up with an existing one, especially if you’re already using GraphQL and/or Webpacker.

If you’re just starting out with Rails, check out the official getting started guide. This walkthrough assumes you have a little familiarity with Rails.

Set up GraphQL

The gem makes setting up our GraphQL API as easy as running a few commands. First, add the gem to your , somewhere above all the declarations:

Then, update your dependencies and generate the GraphQL Ruby boilerplate:

This generator attempts to give you a nice GraphiQL UI to query your API in development, but it didn’t work for me out of the box. I had to add the following lines to . You may have to, too:

Restart your server. If you go to http://localhost:3000/graphiql, you’ll see a GraphiQL console in your browser so you can query your new GraphQL API. Run a query, just to make sure everything’s wired up properly.

Setting up the frontend

Rails uses webpack to compile assets by default. If you’re on Rails 6, it’s likely already set up in your application. To set up TypeScript support, you can use a simple command provided by Webpacker:

This will add some configuration to Webpacker to enable TypeScript support. Let’s also convert Rails’ generated JavaScript file to TypeScript and delete the example file the generator produced:

Next, let’s set up a blank page just so we have a place to run our JavaScript. If you already have such a route, skip this part. For brevity, we’ll use , but in production you’d be best off making a controller specifically to serve your frontend. Any actions placed in will be inherited by all controllers in your application, which could lead to chaos.

In , create the empty controller action:

Create a (mostly) empty view:

Then define a route that sends requests to to this route in

Here you can also spot how, earlier, the generator added a GraphQL endpoint and the GraphiQL page.

Finally, let’s add some code to to verify things are working. Rails also starts a few of its built-in library in this file—I’m going to leave those out from now on.

Refresh http://localhost:3000. You should see “Hello, world!” on the page and another, similarly zealous greeting in the console.

You might notice that loading the page took a while. This is because Webpacker is recompiling your whole webpack app on every request. You can instead tell Webpacker to run a separate development server that will recompile only when you change files. In a separate terminal, run this:

If you reload the page, you’ll see it’s super quick now. Rails automatically defers asset requests to the webpack development server rather than triggering a webpack compilation on every request.

Making GraphQL calls from our frontend

Let’s have our component actually run a GraphQL request against our API. First, install the Apollo client:

This comes with TypeScript typings for free. In fact, everything in Apollo’s ecosystem is written in TypeScript. You may have noticed we also added —we won’t actually be using React in this tutorial, but the Apollo client is a React client. If this doesn’t work for you, you’re welcome to use any one of Apollo’s integrations with other frameworks.

Let’s set up the client in :

We’re extracting Rails’ CSRF token from the page and sending it along with the GraphQL requests—it’s one cool advantage of serving our frontend from Rails. An alternative strategy is to disable CSRF protection within .

Then, below that, we’ll tell the client to make a GraphQL request:

If you refresh the page, you should see the GraphQL API call result in the console.

Two type systems: GraphQL and TypeScript

This section is the crux of this guide. Look in —TypeScript has no idea what looks like. It could be an object, array, string, even a function—its type is , and is dangerous in this context.

Imagine you’re querying a user, but forget to ask for their name. Wouldn’t you like to know that will never exist? If we had more complete types, such a mistake would be stopped at build time.

We need a way to know the shape of this response. We could generate it manually, but that’s tiresome, and feels silly when we have a GraphQL schema with sound typing.

Apollo codegen

Enter . It’s a code generator that can take a GraphQL schema, scan a repository for GraphQL queries, and turn them into type declarations. It supports TypeScript, Flow, and Swift.

Export the schema to SDL

First, we need our schema in a format Apollo understands. We can’t just pass it our Ruby file—Apollo doesn’t integrate at all with the GraphQL gem. Instead, we can tell GraphQL Ruby to output our schema in the GraphQL SDL (Schema Definition Language), which Apollo does understand. From there, Apollo can connect our schema to our queries and generate typings for each query.

The first step is getting GraphQL Ruby to give us the schema. Create and define this task inside of it:

GraphQL Ruby’s method returns a string containing exactly what we need—our schema in SDL. Let’s run this new rake task:

This creates , our schema in pure GraphQL SDL.

Set up the Apollo CLI

Now that this exists, we can easily tell the Apollo CLI how to generate types. Let’s first install it:

Then we’ll make a new file at the root of our directory, , and tell it how to find everything we’ve built and where to put types. Again, replace with your schema name.

From top to bottom, we have:

  • : where to put types
  • : stick all generated types into one file
  • : where to find all the queries we’re writing
  • : the name of the function we wrap our queries in
  • : the name of the schema
  • : the location of the file we generated in the previous step

Lastly, we’ll set up an NPM script to run our Rake task and the Apollo codegen. In , add the following:

Let’s run it:

And now, with very little work, you’ve got types inside of for each of your queries. The name of each file (and interface inside each file) is the same as the name of your particular GraphQL query.

You can run this command whenever you make changes to your schema, or use something like Guard (Ruby) or Watchman (Node) to run it whenever relevant files change. I used Guard to run this command whenever and/or changes.

You should also run this command in CI whenever you compile your TypeScript—it’ll ensure your types are up-to-date before checking them.


Tying it all together

At last, we have all the pieces we need to add types to our GraphQL query. Open up again, and add the types that were generated.

Merely the fact that we wrote a query named caused its return type to be generated. This is great for us, because it means we can ensure that is exactly the shape it will actually be in production.


Closing thoughts

  • I’ve made an example repository with this setup. Feel free to clone, alter, open issues, turn it into a generator—whatever!

  • The natural continuation of this is to start building React components. For that, you’ll have to set up Webpacker to support React. You may have to alter after running the React generator.

  • You should also add these generated files (both the TypeScript files and the GraphQL schema) to your .

  • Since we made an , Apollo’s Visual Studio Code extension will work out of the box and annotate your query’s types.

Hope that was fun! If you have any questions or feedback, or just want to know when I write something else, follow me on Twitter.

Steven Petryk

twitter find me on twitter stevenpetryk ↗️

Sours: https://stevenpetryk.com/blog/rails-graphql-typescript-stack/
  1. Full power frost
  2. California obituaries 2015
  3. Combat comic
  4. Tk google cloud
  5. Salicylic acid neutralizer

graphql-ruby Tutorial - Introduction

Motivation

Ruby is general purpose programming language optimized for programmer happiness. One of its most popular frameworks for building web applications is called Ruby on Rails.

The Ruby ecosystem was one of the first to adopt GraphQL. A couple of popular Ruby on Rails applications like Github and Shopify are using it in production already.

In this chapter you’ll learn how to build your very own GraphQL server using the following technologies:

What is a GraphQL Server?

A GraphQL server should be able to:

  • Receive requests following the GraphQL format, for example:
  • Connect to any necessary databases or services responsible for storing/fetching the actual data.
  • Return a GraphQL response with the requested data, such as this:
  • Validate incoming requests against the schema definition and the supported format. For example, if a query is made with an unknown field, the response should be something like:

These are the basic features all GraphQL servers have, but of course, they can do much more as needed. You can read in more detail about the expected behavior of a GraphQL server in the official specification.

Schema-Driven Development

An important thing to note about building a GraphQL server is that the main development process will revolve around the schema definition. You’ll see in this chapter that the main steps we’ll follow will be something like this:

  1. Define your types and the appropriate queries and mutations for them.
  2. Implement functions called resolvers to handle these types and their fields.
  3. As new requirements arrive, go back to step 1 to update the schema and continue through the other steps.

The schema is a contract agreed on between the frontend and backend, so keeping it at the center allows both sides of the development to evolve without going off the spec. This also makes it easier to parallelize the work, since the frontend can move on with full knowledge of the API from the start, using a simple mocking service which can later be easily replaced with the final server.

Sours: https://www.howtographql.com/graphql-ruby/0-introduction/
Using GraphQL in Ruby on Rails applications

Before you can do that To complete this action, sign in to your Community account or create a new one.

The author selected Free Press to receive a donation as part of the Write for DOnations program.

Introduction

GraphQL is a strongly typed query language for APIs and a server-side runtime for executing those queries with your existing data. GraphQL allows clients to fetch multiple resources from the server in a single request by giving clients the ability to specify the exact data needed in the query. This removes the need for multiple API calls. GraphQL is language and database independent, and thus can be implemented in almost every programming language alongside any database of choice.

In this tutorial, you will build a GraphQL-powered Ruby on Rails API for taking notes. When you are finished, you will be able to create and view notes from the API using GraphQL.

GraphiQL IDE

If you would like to take a look at the code for this tutorial, check out the companion repository for this tutorial on the DigitalOcean Community GitHub.

Prerequisites

To follow this tutorial, you’ll need:

Step 1 — Setting Up a New Rails API Application

In this step, you will set up a new Rails API application and connect it to a PostgreSQL database. This will serve as the foundation for the note-taking API.

Rails provides commands that make building modern web applications faster for developers. These commands can perform actions that range from creating a new Rails application to generating files required for app development. For a full list of these commands and what they do, run the following command in your terminal window:

This command yields an extensive list of options you can use to set the parameters of your application. One of the commands listed is the command, which accepts an and creates a new Rails application at the specified path.

Create a new Rails application using the generator. Run the following command in your terminal window:

This creates a new Rails application in a directory named and installs the required dependencies. Let’s go over the flags associated with the command:

  • The flag pre-configures the application with the specified database.
  • The flag instructs Rails to not generate test files since you won’t be writing tests in this tutorial. You can also use this flag if you plan to use a different testing framework other than the one provided by Rails.
  • The flag configures a Rails application with only the files required for building an API with Rails. It skips configuring settings needed for browser applications.

Once the command is done running, switch to the newly created directory, which is the application’s root directory:

Now that you have successfully set up a new Rails API application, you have to connect it to a database before you can run the app. Rails provides a file found in , which contains configurations for connecting your app to a different database for different development environments. Rails specifies a database name for different development environments by appending an underscore () followed by the environment name to your app’s name. You can always change any environment database name to whatever you choose.

Note: You can alter to choose the PostgreSQL role you would like Rails to use to create your database. If you created a role that is secured by a password, follow the instructions in Step 4 of How To Use PostgreSQL with Your Ruby on Rails Application on Ubuntu 18.04 or How To Use PostgreSQL with Your Ruby on Rails Application on macOS to configure your role.

Rails includes commands for creating and working with databases. With your database credentials in place, run the following command in your terminal window to create your databases:

The command creates a and database based on the information provided in the file. Running the command yields the following output:

With your application now successfully connected to a database, you can test the application to ensure it works. Start your server with the following command if you are working locally:

If you are working on a development server, you can start your application by specifying the IP address the server should bind to:

Note: The server listens on port . If you’re working on a development server, ensure that you have opened port in your firewall to allow connections.

The command launches Puma, a web server for Ruby distributed with Rails. The command binds the server to any IP you provide.

Once you run this command, your command prompt will be replaced with the following output:

To run your application, navigate to or in your browser. You’ll see the Rails default welcome page:

Rails welcome page

The welcome page means you have properly set up your Rails application.

To stop the server, press in the terminal window where the server is running.

You have successfully set up a Rails API application for a note-taking API. In the next step, you will set up your Rails API application to receive and execute GraphQL queries.

Step 2 — Setting Up GraphQL for Rails

In this step, you will configure your Rails API application to work with GraphQL. You will install and set up the necessary gems required for GraphQL development in Rails.

As previously mentioned, GraphQL is language agnostic and is implemented in many programming languages. The graphql-ruby gem is the Ruby implementation for GraphQL. GraphQL also provides an interactive in-browser IDE known as GraphiQL for running GraphQL queries. The gem helps you add GraphiQL to your development environment.

To install these dependencies, open the project’s for editing, using nano or your favorite text editor:

Add the and gems to your Gemfile. You can add the gem anywhere, but the gem should be added under the development dependencies:

~/rails_graphql/Gemfile

Save and close the file when you are done adding the gems.

In your terminal window, use the following command to install the gems:

The output shows that the gems are installed.

The gem provides generators to create various files. To view the available generators, run the following command in your terminal window:

The generators prefixed with are the ones associated with the gem.

You will use the command to add boilerplate code to the application and mount GraphiQL in your development environment. The boilerplate code will include all the files and directory needed for the gem to work with Rails.

In your terminal window, run the following commands:

This command generates several files, including a file located at and a directory at which contains files required to get started with GraphQL in Rails. It also adds a HTTP route in the routes file located at . This route is mapped to the method which handles all queries to the GraphQL server.

Before you can test the GraphQL endpoint, you need to mount the GraphiQL engine to the routes file so you can access the GraphiQL in-browser IDE. To do this open the routes file located at :

Add the following code to the file to mount the GraphiQL engine in the development environment:

~/rails_graphql/config/routes.rb

This mounts the GraphiQL engine to the path and directs all queries to the method.

Since this is an API application created with the flag, it does not expect to render any page in the browser. To make the GraphiQL editor show up in the browser, you need to make a couple of small changes to your application’s configuration.

First, open the file located at :

Next, uncomment the line:

~/rails_graphql/config/application.rb

Save and close the file after uncommenting the line.

Now create a directory at :

Next, create a file in the newly created directory. The file specifies additional assets to be compiled and made available to the browser:

Add the following code to the file which tells Rails to precompile the and files so Rails can serve them to your browser:

~/rails_graphql/app/assets/config/manifest.js

Save and close the file.

With that done, you can test your GraphQL endpoint. Restart your development server, and in your browser, navigate to or . The GraphiQL query editor displays in your browser:

GraphiQL IDE

The left side of the GraphiQL IDE accepts GraphQL queries and the right side displays results of the run query. The GraphiQL query editor also has a syntax highlighter and a typeahead hinter powered by your GraphQL Schema. Together, these help you make a valid query.

To try a example, clear out the default text in the editor’s left pane and type in the following query:

Click the Play icon button in the header and you’ll recieve a successful response on the screen, as shown in the following figure:

GraphiQL IDE Response successful response

You have successfully set up your Rails API application to work with GraphQL and tested your GraphQL endpoint to confirm it works. In the next step, you will create GraphQL types for your application.

Step 3 — Creating Types for the Application

GraphQL depends on its Types and Schema to validate and respond to queries. In this step, you will create a Note model and the GraphQL types required in your note-taking API.

A GraphQL type consists of and which, in turn, define the fields and arguments that can appear in any GraphQL query that operates on that type. These types make up a GraphQL Schema. GraphQL defines the following types:

  • The Query and Mutation types: These are special types that define the entry point of every GraphQL query. Every GraphQL service has a type and may or may not have a type.
  • Object types: These are the basic components of a GraphQL schema. These represent the objects you can fetch from a GraphQL service and the fields each object holds.
  • Scalar types: These are default types that come with GraphQL out of the box. They include , , , , and .
  • Enumeration types: These are types that define a particular set of allowed values.
  • Input types: These are similar to object types, with the only difference being that they define objects that you can pass to queries as arguments.

There are other types, including , , , and . You can find a list of available GraphQL types in the official GraphQL documentation.

For this application, you will create a model and a object and input type. The model will represent the database table that will store your notes while the object and input type will define the fields and arguments that exists on a object.

First, create a model using the subcommand provided by Rails and specify the name of the model along with its columns and data types. Run the following command in your terminal window:

This command creates a model with two fields: , with the type , and , with the type . The command also adds a database on the column. It generates these two files:

  • A file located at . This file will hold all model-related logic.
  • A file (the number at the beginning of the file will differ, depending on the date you run the command) located at . This is a migration file that holds the instruction for creating a corresponding table in the database.

To execute the instructions in the migration file, you’ll use the subcommand which executes the instruction in your migration files. Run the following command in your terminal window:

Once the command runs successfully, you will see output similar to the following:

With the note model in place, next you’ll create a . A valid note object is expected to have an , a , and . Run the following command in your terminal window to create a :

The command instructs Rails to create a GraphQL object type called with three fields: an field with a type of , and the and fields, each with a type. The exclamation point () appended to the field type indicates that the field should be non-nullable, meaning that the field should never return a null value. Non-nullable fields are important, as they serve as a form of validation that guarantees which fields must be present whenever GraphQL objects are queried.

Running the preceding command creates a file located at containing a class with three non-nullable fields.

Lastly, you will create a type to define the arguments required to create a note. Start by creating an directory under . The input directory will house input types:

Note: It’s not a requirement to create input types in the input directory; it is merely a common convention. You can decide to keep all your types under the types directory and exclude nesting the class under an module whenever you’re accessing it.

In the directory, create a file:

Add the following code to the file to define the fields for the type:

~/rails_graphql/app/graphql/types/input/note_input_type.rb

In the file, you added a class that inherits from the class and accepts two required arguments; and , both of a string type.

You’ve created a model and two GraphQL types for your note-taking app. In the next step, you will create queries to fetch existing notes.

Step 4 — Creating Queries for the Application

Your GraphQL-powered API is gradually coming together. In this step you’ll create two queries; one to fetch a single note by and another to fetch all notes. The GraphQL type handles the fetching of data and can be likened to a GET request in REST.

First, you’ll create a query to fetch all notes. To start, create a directory to house all queries:

In the directory, create a file from which all other query classes will inherit:

Add the following code to the file to create a class that other query classes will inherit from:

~/rails_graphql/app/graphql/queries/base_query.rb

In the file, you added a class that inherits from the class. The class is a container that can hold logic belonging to a . It can be attached to a with the keyword.

The class can also contain any code you intend to reuse across multiple query classes.

Next, create a file in the directory. This file will hold the logic for fetching all existing notes, and will be attached to a in the query type file:

Add the following code to the file to define the return object type and resolve the requested notes:

~/rails_graphql/app/graphql/queries/fetch_notes.rb

In the file, you created a class that inherits the previously created. The class has a return declaration that declares that the data returned by this query should be an array of the already created .

The also contains a method that returns an array of all existing notes sorted by their created date in descending order.

The query is ready to receive and return requests for notes, but GraphQL is still unaware of its existence, to fix that, open the GraphQL query type file located at :

The file is the entry point for all GraphQL types. It holds the query fields, and their respective resolver methods. Replace the sample code in the file with the following:

~/rails_graphql/app/graphql/types/query_type.rb

In the file, you added a field and attached it to the class using a . This way whenever the query is called, it executes the logic in the method of the class.

In order to test your query, you need some data to fetch, but you currently don’t have any notes in your database. You can fix that by adding some seed data to your database. Open the file located at :

Add the following code to the file to create five notes:

~/rails_graphql/db/seeds.rb

Save and close the file after adding the code.

Open your project’s root directory in another terminal window and run the following command to run the code in the file:

This creates 5 notes in the database.

With data in your database, and your development server running, navigate to or in your browser to open your GraphiQL IDE. In the left side of the editor, type in the following query:

This GraphQL query declares a operation, indicating you want to make a query request. In the query operation, you called a field that matches the query field declared in the API, and included the fields on a note that you want to be returned in your response.

Click the Play icon button in the header. You’ll see a response similar to the following in the output pane:

The response contains an array of 5 notes that match the fields declared in the query on the left. If you remove some fields in the query on the left side of the editor and re-run the query, you get a response with only the fields you requested. That’s the power of GraphQL.

Next, you’ll create another query to fetch notes by . This query will be similar to the query, only that it’ll accept an argument. Go ahead and create a file in the queries directory:

Add the following code to the file to find and return a note with the provided :

~/rails_graphql/app/graphql/queries/fetch_note.rb

This defines a class that inherits from the class. This class not only returns a single item that must be of a , it also accepts an argument with an type. The method receives the provided argument, then finds and returns a note with the provided . If no note exists or an error occurs, it is rescued and returned as a .

Next, you will attach the class to a query field in the query type file. Open the file in your editor:

Add the following code to the file which defines a resolver for :

~/rails_graphql/app/graphql/types/query_type.rb

To test your new query, ensure your server is running and navigate to or in your browser to open your GraphiQL IDE. In the left side of the editor, type in the following query:

This query operation requests a field, which corresponds to the query field, and is passed an argument. It specifies that we want three fields to be returned in the response.

Run the query by clicking the Play icon button in the header. You will get a response like the following in the output pane:

The response contains a single note that matches the requested with fields matching the ones in the request.

In this step, you created GraphQL queries to fetch notes from your API. Next you’ll write mutations to create notes.

Step 5 — Creating GraphQL Mutations to Modify Notes

In addition to queries, GraphQL also defines a type for operations that modify server-side data. Just as REST provides , , , and requests for creating, updating and deleting resources, GraphQL’s type defines a convention for operations that cause writes on the server-side. In this step, you’ll create a mutation for adding new notes.

includes two classes for writing mutations. They are:

  • GraphQL::Schema::Mutation: This is the generic base class for writing mutations. If you don’t want an argument required in your mutations, you should use this class.
  • GraphQL::Schema::RelayClassicMutation: This is a base class with some conventions; an argument called that is always inserted to the response, and mutations that accepts one argument called . This class is used by default when you use the to add boilerplate GraphQL files to your project.

Create an file in the directory located at :

Add the following code to the file to define the mutation for adding new notes:

~/rails_graphql/app/graphql/mutations/add_note.rb

This defines a class that inherits from the class, which is one of the classes created when you ran the while installing the GraphQL-Ruby gem. The class receives an with the name and a type of , which you created in Step 3. It also returns a called that must be a non-null type.

The method of the class receives the and converts it to a hash which it uses to create and return a new hash containing the new note. If there’s an error while creating the note, the error is rescued and returned as a .

Note: The method in a mutation must return a hash whose symbol matches the names.

Like with queries, the mutation has to be attached to a mutation field using the keyword.

Open the mutation type file located at in your editor:

Replace the code in the file with the following code, which adds a field for the with its corresponding mutation class:

~/rails_graphql/app/graphql/types/mutation_type.rb

In this code, you added an field to the mutation type file and attached it to the class using the keyword. When the mutation is called, it runs the code in the method of the class.

To test your new mutation, navigate to or in your browser to open your GraphiQL IDE. In the left side of the editor, type in the following query:

This declares a mutation operation with an field that accepts a single argument, which in turn accepts a object with keys that match the . The mutation operation also includes a field that matches the field returned by the class.

Run the mutation in GraphiQL and you’ll see the following results in the output pane:

The response returned is the newly created note with the fields requested in the mutation request.

With your mutation now working, your API can fetch and create notes using GraphQL queries and mutations.

Conclusion

In this tutorial, you created a note-taking API application with Ruby on Rails using PostgreSQL as your database and GraphQL as your API query language. You can learn more about GraphQL on its official website. The GraphQL-Ruby gem website also contains some guides to help you work with GraphQL in Rails.

Sours: https://www.digitalocean.com/community/tutorials/how-to-set-up-a-ruby-on-rails-graphql-api

Graphql rails

graphql graphql-ruby

CI SuiteGem Version

A Ruby implementation of GraphQL.

Installation

Install from RubyGems by adding it to your , then bundling.

Getting Started

After this, you may need to run again, as by default graphiql-rails is added on installation.

Or, see "Getting Started".

Upgrade

I also sell GraphQL::Pro which provides several features on top of the GraphQL runtime, including Pundit authorization, CanCan authorization, Pusher-based subscriptions and persisted queries. Besides that, Pro customers get email support and an opportunity to support graphql-ruby's development!

Goals

  • Implement the GraphQL spec & support a Relay front end
  • Provide idiomatic, plain-Ruby API with similarities to reference implementation where possible
  • Support Ruby on Rails and Relay

Getting Involved

Sours: https://github.com/rmosolgo/graphql-ruby
How To Use GraphQL with Ruby on Rails - Part 1 - Backend

GraphQL on Rails: From zero to the first query

A hitchhiker’s guide to developing GraphQL applications with Rails on the backend side and React/Apollo on the frontend side. Follow this multi-part tutorial to learn both the basics and the advanced topics by example and feel the power of this modern technology.

GraphQL is one of those new shiny things we’ve been seeing here, there and everywhere: blog posts, conferences, podcasts, maybe, even newspapers. It sounds like you should hurry up and start rewriting your application in GraphQL instead of REST as soon as possible, right? Not exactly. Remember: there is no silver bullet. It’s essential to understand the pros and cons of the technology before making a paradigm-shift decision.

Check out Part 2 and Part 3

In this series, we’re going to walk you through a no-frills guide to the development of GraphQL applications, talking about not only its advantages but also its caveats and pitfalls (and, of course, the ways to deal with them).

GraphQL in a nutshell

There are a lot of other query languages, for instance, SQL and XPath.

According to the specification, GraphQL is a query language and runtime (or execution engine). Query language, by definition, describes how to communicate with an information system. Runtime is responsible for fulfilling queries with data.

At the core of every GraphQL application lies a schema: it describes the underlying data in the form of a directed graph. The runtime must execute queries according to the schema (and some general rules from the specification). That means, every valid GraphQL server runs queries in the same manner and returns data in the same format for the same schema. In other words, the schema is everything clients should know about the API.

Here is an example of a simple GraphQL query:

Let’s dissect it line by line:

  • We define a named query ( is the operation name) accepting a single argument (). The operation name is optional, but it helps readability and could be used by frontend for caching.
  • We “select” the  field from the “root” of the schema and pass the  value as an argument.
  • We describe the fields we want to fetch: in this case, we want to get the  and  of the product as well as the  of the manufacturer.

Essentially, a query represents a sub-graph of the schema, which brings the first benefit of GraphQL—we can fetch only this data we need and all we need at once, in a single query.

Check out this Stack Overflow post to learn more about both overfetching and underfetching.

This way, we solve one of the common problems of the traditional REST APIs—overfetching.

Another noticeable feature of GraphQL schemas is they are stronglytyped: both client and runtime ensure that the data passed is valid from the perspective of the application’s type system. For example, if someone mistakenly passes a string value as the  to the query above, the client fails with the exception without even trying to perform a request.

There are plenty of tools to convert a schema into an interactive documentation website, standalone (e.g., GraphiQL or graphdoc) and framework-specific (e.g., Apollo DevTools).

And the last but not least bonus is a schemaintrospection: clients can learn the API from the schema itself, without any additional documentation sources.

We’ve just learned a bunch of theoretical aspects of GraphQL. Now it’s time to do some coding exercises to make sure you won’t forget everything tomorrow’s morning.

What are we going to build?

During this series, we will be building an application representing a “Martian Library”—a personal online collection of movies, books, and other art objects related to the Red Planet.

The application we are going to build

For this tutorial, we’re going to use:

You can find the source code here—don’t forget to run before the first run. Master represents a current state of the project.

Setting up a new Rails project

If at the time of reading this article Rails 6.0 hasn’t been released yet, you might need to install the release candidate first:

Now we’re ready to run this unexpectedly long command:

We prefer okonomi to omakase: skip frameworks and libraries we don’t need, choose PostgreSQL as our database, preconfigure Webpacker to use React, and skip tests (don’t worry–we’ll add RSpec soon).

Before you start, it’s strongly recommended that you disable all the unnecessary generators in the  :

Preparing the data model

We need at least two models to start:

  • to describe any entity (book, movie, etc.) that we want to store in the library
  • to represent the application user who can manage items in the collection.

Let’s generate them:

Don’t forget to add the  association to :

Let’s add some pre-generated data to :

Finally, we’re ready to initialize our database:

Now that we’ve put some information into our system, let’s add a way to access it!

Adding a GraphQL endpoint

For crafting our GraphQL API, we will use the graphql-ruby gem:

You might be surprised by the number of files a minimal application requires: this boilerplate is the price we pay for all the goodies we described above.

The result of executing generator

First of all, let’s take a look at the schema, :

The schema declares that all the queries should go to  while mutations should go to . We’re going to dig deeper into mutations in the second part of the series; the goal of this article is to learn how to write and execute queries. Thus, let’s open the  class—it is an entry point for all the queries. What’s inside?

It turns out that is just a regular type: it inherits from the  (which we will use as a base class for all types), and it has field definitions–the nodes of our data graph. The only thing that makes different is that GraphQL requires this type to exist (while mutation and subscription types are optional).

Have noticed that the code above is actually a “hello world” app? Before going further (and boring you with by the amount of code), we’d like to show you how to get this “hello world” in your browser.

Let’s see what has been added to the  file by the generator:

Mounting allows us to test our queries and mutations using a web interface called GraphiQL. As we discussed in the introduction, the schema can be inspected, and GraphiQL uses this feature to build interactive documentation for us. It’s time to give it a shot!

Open up http://localhost:3000/graphiql in the browser:

GraphiQL UI

In the left pane, you can type a query to execute, then click the “play” button (or hit Ctrl/Cmd+Enter) and get the response in the right pane. By clicking the “Docs” link at the right top corner, you can explore your schema.

Let’s take a look at our logs—we want to know what happens when we click the execute button.

Execution logs

GraphQL is transport-agnostic, but most implementations, including , use HTTP POST requests.

Requests are sent to , which has also been added to the application by the  gem generator.

Take a look at the  action:

This action calls the  method with the following parameters:

  • and  represent a query string and arguments sent by a client respectively;
  • is an arbitrary hash, which will be available during the query execution everywhere;
  • picks a named operation from the incoming request to execute (could be empty).

All the magic happens inside this method: it parses the query, detects all the types that should be used for building the response, and resolves all the requested fields. The only thing we need to do is to define the types and declare how fields should be resolved.

What’s in the Martian Library?

Let’s move from “hello world” to something real: remove the example contents from and register a field called which will return all the items from the library. We also need to add a resolver method for this field (the resolver method name must match the field name):

Each field definition contains a name, a result type, and options; is required and must be set to either or . We also define optional —it’s a good practice to add a human-readable message to a field: it will be automatically added to documentation providing more context to developers. The array notation for the result type, , means that the field value must be an array and each element of this array must be represented using the  type.

But we haven’t defined yet, right? Hopefully, the  gem will give us a handy generator:

Now we can modify the newly created to our liking.

You may use UUID as a primary key in your table if you are worried about exposing sequential IDs, but let’s go with the easy way now.

As you can see, we’re exposing three fields in :

  • non-nullable fields and 
  • a nullable field

Our execution engine resolves fields using the following algorithm (slightly simplified):

  • First, it looks for the method with the same name defined in the type class itself (like we did earlier in the  for ); we can access the object being resolved using the  method.
  • If no such method defined, it tries to call the method with the same name on the  itself.

We do not define any methods in our type class; thus, we assume that the underlying implements all the fields’ methods.

Go back to http://localhost:3000/graphiql, execute the following query, and make sure that you get the list of all items in response:

So far, we haven’t added any functionality that leverages the power of graphs—our current graph’s depth is one. Let’s grow the graph by adding a non-primitive node to , for example, a  field to represent the user who created the item:

Let’s repeat the same generator spell to create a new type class:

This time we also want to add a computed field—:

Let’s transform our query to fetch users along with items:

At this point, we’re ready to move our attention from the backend side to the frontend side. Let’s build a client for this API!

Configuring the frontend application

If you’re using Google Chrome, mind installing the Apollo extension.

As we already mentioned, we recommend you install the Apollo framework for dealing with GraphQL client-side.

To get the ball rolling, we need to install all the required dependencies:

We didn’t include any package-adding types to JS (TypeScript or Flow) so as not to make this tutorial too complicated. In practice, using typed JS with GraphQL makes a lot of sense since we already have a types system. For instance, apollo-codegen can generate type signatures automatically.

Let’s take a look at some of the installed packages:

  • We use to build our first queries.
  • is a generic framework-agnostic package for performing and caching GraphQL requests.
  • is a storage implementation for Apollo cache.
  • contains a set of React components for displaying data.
  • and other links implement a middleware pattern for operations (you can find further details here).

Now we need to create an entry point for our frontend application. Remove from the  folder and add :

For the sake of debugging, it’s good enough to stay with the following content:

Let’s generate a controller to serve our frontend application:

Update to contain the React root element and a link to our pack:

Finally, let’s register a new route in the :

Restart your Rails server and make sure you see the ghost in the browser console. Don’t be scared.

Configuring Apollo

Create a file for storing our application’s Apollo config:

In this file we want to configure the two core entities of the Apollo application, the client and the cache (or more precisely, the functions to create both):

Caching is one of the most powerful features of Apollo. Sometimes it becomes overpowering, and you might prefer a more straightforward solution. In any case, it’s worth learning how it works. Find more details about how Apollo caching works here.

Let’s take a second and look at how cache works.

Each query response is put into the cache (the corresponding request is used to generate the cache key). Before making a request, ensures that the response hasn’t been cached yet, and if it has been–the request is not performed. This behavior is configurable: for instance, we can turn off caching for a particular request or ask the client to look for a cache entry of a different query.

One important thing we need to know about the cache mechanism for this tutorial is that, by default, a cache key is a concatenation of the object and . Thus, fetching the same object twice would result only in one request.

Back to coding. Since we use HTTP POST as a transport, we need to attach a proper CSRF token to every request to pass the forgery protection check in the Rails app. We can grab it from (which is generated by ):

Let’s look at how we can log errors:

In production, it makes more sense to use an exception tracking service (e.g., Sentry or Honeybadger): just override the  function to send errors to the external system.

We’re almost there—let’s tell the client about the endpoint for making queries:

Finally, we’re ready to create an Apollo client instance:

The very first query

We’re going to use a provider pattern to pass the client instances to React components:

It’s the first time we are using an  component from the  library:

Let’s change our to use the newly created provider:

If you use you may need to import to use all the cool JavaScript tricks like async/await. Don’t worry about the polyfill’s size; will remove everything you don’t need.

Let’s create a  component to display the list of items on the page:

We’re going to use the  component from , which accepts the  string as a property to fetch the data on mount:

We can access the loading state and loaded data through the corresponding and  properties (passed using a so-called render-props pattern).

Don’t forget to add the component to the main page:

If you reload the page you should see the list of items with emails of users who added them:

List of items with emails of users

Congratulations! You’ve just made the very first step towards GraphQL happiness!

…And the very first problem

Everything seems to work fine, but let’s take a look at our server logs:

N + 1

The SQL query is executed four times, which means that we hit the famous N+1 problem–server makes a query for each item in the collection to get the corresponding user info.

Before fixing the problem, we need to make sure that it’s safe to make code modifications without breaking anything—let’s write some tests!

Writing some specs

Since we’re running on the edge, we need the upcoming version of the gem.

Now it’s time to install and configure RSpec, or more precisely, the  gem:

To make it easier to generate data for tests let’s install factory_bot:

Make factory methods (, , etc.) globally visible in tests by adding to the .

Since we created our models before adding Factory Bot, we should generate our factories manually. Let’s create a single file, , for that:

Now we are ready to write our first test. Let’s create a spec file for :

The simplest query test looks like this:

Testing GraphQL might feel repetitive. Should it happen to you—consider using fixtures and the fixturama gem.

First, we create a pair of items in our database. Then, we define the query under test and the subject () by calling the  method. Remember, we had a similar line in the ?

This example is very straightforward: we’re not passing either or  to the  call, though we definitely can do that if needed.

Now we’re confident enough to fix “the bug”—the N+1 problem!

GraphQL vs. N+1 problem

The easiest way to avoid N+1 queries is to use eager loading. In our case, we need to preload users when making a query to fetch items in :

This solution can help in simple situations, but it’s not very efficient: the following code preloads users even if the client does not need them, e.g.:

Learn how to solve the N+1 problem using batch loading by example from the “Active Storage meets GraphQL” post in our dev.to blog.

Discussing other ways to solve the N+1 problem is worthy of a dedicated post and out of this tutorial’s scope.
Most of the solutions fit the following two groups:


That’s all for today! We learned a lot about GraphQL, completed all the routine work of configuring the backend and frontend applications, made the first query, and even found and fixed the first bug. And that’s just a tiny step (despite the size of the article) in our journey. We’ll come back shortly and unveil how to manipulate data using GraphQL mutations and keep it up-to-date with subscriptions. Stay tuned!

Part 1 | Part 2 | Part 3

Sours: https://evilmartians.com/chronicles/graphql-on-rails-1-from-zero-to-the-first-query

Similar news:

This is going to be a long post about , that is then on the frontend. All within the same application.

I believe this is a good choice of stack for two main reasons:

  1. GraphQL provides a much cleaner query API than a REST API does -- especially because I can request only the data I want at any time.
  2. With TypeScript and another utility called , I can ensure the types that are served by my API match exactly to the types used in my frontend at all times.

For this post, all the code is written from the perspective of having just run the command to create a new Rails application:

There's no fancy configuration here, just plain Rails.

This guide is written in a way that should allow you to apply the same concepts to your existing applications if you have one of those you want to use.

This guide is for intermediate Rails developers, and it will gloss over a few of the fundamental Rails concepts such as models, migrations, views, routes, and controllers. I will assume you know those by now.

What won't be glossed over is the GraphQL, TypeScript, React, and Apollo setup. After all, that's why you're reading this post in the first place.

This guide will feel long, but most of it talks about things that are a one-time setup cost. We pay this large cost now so that our application is easier to develop over the much, much longer term.

JavaScript? In my Rails app?

For a long time, there have been efforts to have one flavor or another of JavaScript be a part of Rails itself. It started out with Prototype.js, and moved onto jQuery, to now a situation where Rails does not thrust its opinion of a JavaScript framework into your application -- you're free to choose.

There has been a modern push for Rails applications to integrate further with modern JavaScript frameworks, such as React and Vue. Nowhere is this more evident than the fact that modern Rails applications now include a gem called . This gem provides an interface between the Rails application and any dependencies provided from the frontend by Webpack.

You can specify your JavaScript dependencies in files called packs, and then load those into your Rails application using ERB code.

Brand new Rails applications have a file called :

While these dependencies are typically JavaScript, although they could be CSS or images too.

This file is served out of the application via this line in the application layout ():

When you run and access , Rails will compile the Webpack assets and serve them through the Rails server itself.

What about webpack-dev-server?

If you don't want to wait for a request to tell Webpack to compile assets, you can run as a separate process and your assets will be compiled as soon as they change, rather than whenever the page is refreshed. The difference is usually about half a second, but in larger applications, it can be much longer than that. My advice would be to always rely on .

This will be the file that will be the place we load our JavaScript into our application. It doesn't have to be the only place, we could in fact spread this out over multiple different packs, if we wished:

If we approached it this way, then we could split our code up and only load whatever assets we needed to load on certain pages. Perhaps we only want to load the content on some pages and the code on others.

For this tutorial, we'll stick with one file.

We'll return to this file later on when we implement some frontend code. Before we need to build our frontend, we'll need to serve some data out of our Rails application. And to serve that data, we're going to use GraphQL.

Setting up our Rails application

Before we get to setup GraphQL, we'll quickly setup a model and some data in our database. Then we'll get to GraphQL.

Model setup

Let's begin by creating a new model within our application and migrating the database to create the table for that model:

After this, we'll need to create at least one book so that we have some data to be served through the API:

With our model setup with some data, we can now add our GraphQL API, which will serve this data for the model.

Setting up GraphQL

There is a gem called , which has all the things we need to have to use GraphQL within our Rails application. Let's add it as a dependency of our application now:

Next up, we can run the installer that comes with that gem:

This will setup a few classes within our application, and we will use some of these in this guide. Of note are:

  • - Where the GraphQL schema for our application is defined.
  • - Where fields for GraphQL queries are defined.
  • - Where fields for GraphQL mutations are defined.

At the end of this setup, we will see this message:

Gemfile has been modified, make sure you

The modification that has been made is that another gem called has been added to our Gemfile, along with two routes to . Let's run before we forget to:

The two routes that were added to are:

The first route enables GraphiQL, a graphical interface to GraphQL, which is then accessible in our application at http://localhost:3000/graphiql.

The second route is where our GraphiQL and our frontend will send GraphQL queries to. The was generated for us by the earlier invocation.

Let's now setup a GraphQL object to represent books in our GraphQL API by running this command:

This command will inspect our model and create a GraphQL type that exposes all attributes from that model. It create this file at :

We could remove any of these fields if we did not want to expose them through the GraphQL API. But for now, we'll keep them.

To use this type, we can declare a field over in . We'll delete the example one that is there at the moment and turn this file into this:

Fetching all records from a database considered dangerous

Fetching all the records at once in a table might mean you end up with a lot of records. In the past, you might've used something like will_paginate or Kaminari to do pagination in your application.

GraphQL uses connections for this, but we will not be covering that in this guide.

This will allow us to query our GraphQL endpoint and retrieve all the books by using this GraphQL query through GraphiQL:

Well, we could! But is currently broken with Rails 6.

GraphiQL workaround

The version of GraphiQL bundled with is broken, and so we will need to work around this problem.

Let's start by removing that gem from the :

And we'll remove the route from as well:

An alternative app we can use is the GraphQL playground. Download a version for your OS from the releases page on GitHub.

To make this application work with our Rails application, we'll need to make one little change in the . We need to uncomment the line and turn it into this:

This will ensure that local requests -- requests from the same machine the application is running on are allowed through, but everything else must send through a CSRF token. Our Rails application provides the CSRF token, and we'll see that used a little later on when we get to using Apollo. The CSRF token will be validated by Rails, and only requests that use a valid CSRF token will be permitted to make requests to our API.

Load up GraphQL playground and put in as the endpoint. In the left panel, enter:

And then hit the Play button in between the panels. When the request succeeds, we'll see the data come back through from the GraphQL API:

GraphQL Playground

We'll know that we've setup our GraphQL code correctly within the Rails application when we see this. This means we can now proceed to set up the frontend of our application.

TypeScript + React setup

There are two different ways we could go here:

  1. We could add React and Apollo to our application and live a happy and fruitful life using JavaScript.
  2. We could add React, Apollo, and TypeScript to our application and live a happy and fruitful life with an extra guarantee our code will be type-checked, and we won't fall into the easy trap of comparing a string to a number.

I prefer the second route, even if it is a bit more work in the setup. The second path eases the cognitive load involved with remembering the types of things -- like we would have to do in a traditional Ruby or JavaScript application. TypeScript can tell us the types of our variables, especially in an editor like Visual Studio Code, which frankly has excellent TypeScript integration.

On top of this, there is another package we'll use later on called Codegen that will generate TypeScript types for us directly from our Ruby GraphQL API.

TypeScript

To start off, we'll add TypeScript to our Rails application, which we can do by running this command:

This will:

  • Add and as dependencies of our application in . These packages are used to load and parse TypeScript files, and comes with a command called that we can use to check if our code is typed correctly.
  • Create a new file called that contains all the configuration for TypeScript.
  • Configure Webpacker to load TypeScript files (anything ending in or ), and it'll put a new file in called :

We can delete this file, as we won't be needing it.

One extra bit of configuration that we'll need to do here is to set a configuration value in . Add this line to the list:

This will direct the TypeScript compiler to use React when it encounters a JSX tag. For more information about this option, read this documentation page from the TypeScript handbook.

React

Next up, we want to add React to our application. We can do this using a command too.

This command will:

  • Create a file that contains configuration for Babel directing it how to load React components.
  • Create a file at that demonstrates how to use React within our application.
  • Configures to support files ending with
  • Adds the following JS packages:

    This file that was generated contains some code to load a library called: :

    PropTypes allows us to specify the types for React components. A small example of this is available in :

    We will not be using PropTypes in our code because we'll be using TypeScript instead.

    PropTypes has been succeeded by TypeScript.

    Why on earth are there two ways to specify types in a React codebase? That might be what you're thinking right now. A little bit of history is that PropTypes has existed for a long time, while TypeScript has gained preference recently.

    PropTypes allows us to specify that the property for the

    component is going to be a string. When we load this code in the browser, PropTypes will run and then validate that is indeed a string. If it's not, then we'll see an error appear in the developer console (if we're looking there!). This is what we'd call a runtime type check -- the code is type-checked when it runs.

    TypeScript allows us to do the same sort of validation, but it will run at compile time, right when the code is being "translated" into JavaScript for the browser's consumption. This is the right step (imo) because while you're writing the code, you want to know if you've made a mistake -- not when you're running it! The other advantage is that TypeScript has excellent editor integration, and it will warn you about incorrect types. We'll see a few of these examples later on in this guide.

    So let's remove this configuration from , as well as removing the and associated babel plugin:

      This will mean that the packages that have been added by are now just:

        The package configures Babel to parse JSX content into regular JavaScript code, and a few other niceties that we don't need to care about right now.

        The two other packages, and , are the most useful of the lot, as they allow us to use React and have it interact with a page's DOM (Document Object Model). This work is separated into two packages, as React can be used in other contexts outside of a DOM, such as React Native.

        Let's take a closer look at what contains:

        Now that we've taken out the library, we can remove all the propTypes code from this file:

        That's much easier to read now!

        This file imports both the and libraries. We need to import wherever we're using JSX. And we import whenever we want to put a React component somewhere on our page.

        Next, this file defines a small function component, returning a simple with a message inside it.

        Finally, this code waits for the event to be sent out by the browser, and then it will append this component to the tag of whatever page has included this JavaScript.

        Rendering React within Rails

        Let's take a look at how to render this React component within our Rails application.

        To get started, we'll create a new controller, view, and route by running this command:

        The route this generates will be in and will look like this:

        Let's change this to be a route so that we can visit it using http://localhost:3000/ instead of http://localhost:3000/home/index.

        Once this route has been changed, we can go to http://localhost:3000 and see the view that was generated:

        Simple Rails View

        This view is not currently rendering our React component, but we can make it do so by bringing in the file with this addition to :

        When we refresh the page, we'll see the React component appended to the bottom of the page:

        Simple Rails View with React Component

        Excellent! We now have a way to make React components appear on our Rails views.

        However, there's a caveat to this: these components will always appear at the bottom of our pages! If we were to add a footer to the bottom of the tag within our application layout, these React components would appear underneath that footer. That is not ideal!

        What would be better for us is to be able to insert these components wherever we wish on the page. This will enable us to have Rails-generated HTML sitting along-side React components that also generate their own HTML.

        So to work around that, we'll devise a way to mount React components at particular places within Rails views.

        Placeable React components

        What we now want to be able to do is to be able to put a React component anywhere in our view. Let's say that we wanted our "Hello React!" to appear between the and tags in :

        We cannot put there, as the code in will still direct the component to be appended to the tag:

        So what can we do instead?

        Well, another way we can approach this problem is to have our code look for particular types of elements on the page, and then choose to put React components into those particular elements. For example, we can make it so if we were to write this code:

        A component called would be added between those and tags.

        To put this little tag inside our views, we can write a helper in :

        This code in will generate that :

        If we wanted to support parsing properties to this method, we could make this code:

        This takes a list of properties and passes them through as an extra attribute on our , and so allows us to write code such as:

        However, if we go and refresh that page again, we'll see the component is not being rendered in that spot -- it's still being rendered at the bottom of the page:

        Simple Rails View with React Component

        But if we inspect the source code for our page, we'll see that the exists`:

        In order to fix this up, we're going to need to write some additional code that will scan for these tags containing the attribute and then act on those tags.

        Scanning for and mounting React components

        The code that we're going to use to do this scanning and mounting the React components is the most complicated code we'll come across in this guide. Please bear with me! What we'll do here is work on making this code work, then we'll go through it top-to-bottom.

        We'll put this code in a file called :

        And over in , we'll add these lines:

        Lastly, we'll need to export the component from :

        With this change to , we've removed the code that was previously automatically inserting the component at the bottom of the page, and instead, we're now exporting this component and leaving the rendering of that component as something else's job.

        That something else is that code that we wrote. Let's look at that again, step by step:

        This code defines the function that we use in . This function adds an event listener that waits for the event to happen, just like the old code we had in did. Then we go a different path from there. Instead of rendering a specific React component, we're instead going on a search for which ones the page wants us to render. We find all the elements that are "mount points" for our React components by using and looking for those elements that match the CSS selector .

        Let's look at the next couple of lines:

        We attempt to find out their component name for all of the elements mentioned by accessing the property by using a combination of and . Once we have that name, we can then attempt to find that component by a name using . As long as we've chosen to mount a component in with this function, it will be available here.

        Let's look at the final few lines:

        If this code finds a component, it attempts to parse the json contained in . This will pull out the JSON from the attribute on the :

        Then, now that the function has all three of the , the and the determined, it can use to put this code directly onto the page, exactly where we said it should go.

        Let's refresh the page. This time we'll see the component is now in between the and tags:

        "Hello React" is in between the tags

        Hooray! We now have the ability to put our React components wherever we like on the page. This will enable us to intermingle our Rails view code with React components -- we can put static HTML rendered server-side by Rails right next to dynamic HTML rendered client-side by React.

        Books React Component

        Let's now look at something a bit more complex than putting "Hello React!" on the page. This time, we're going to build another component, called . This component will render hard-coded data from a TypeScript file onto tags on the page.

        When we write this file, we'll be declaring types using TypeScript and using those to guide us in what properties are available inside each of the components we build.

        We'll eventually use this file to pull in and display the data from our Rails application's GraphQL. Before we get there, though, it will help to build a scaffold using static data so that we can experiment with it, if necessary.

        Let's create a new file at .

        In this file, we start by importing React. This is necessary because we're using JSX in this file.

        Next, we define the shape of the data that will be coming through to our component, mimicking the shape of the data that GraphQL gives us.

        Next, we define a component that will render a simple tag with a book's title.

        Then we get to the component. This one uses the and variables set outside of the component to pretend like it's loading data from our GraphQL service and then uses the component to render that data.

        Finally, the component is exported as the default export. This will allow us to import it into other files using .

        What we have here are the barest of bones required to render data using React and TypeScript in our application. This is just a few steps up from our "Hello React" example and moves us closer towards having this frontend talk to our GraphQL backend.

        Mounting the Books component

        To use this component, we will need to mount it within our application. We can do this by going to and changing the end of that file to this:

        This will now automatically render our and components whenever they're requested through our application.

        To request the component to be rendered, we'll go over to and add this line in:

        Now when we refresh this page, we'll see our (very short!) list of books:

        List of books

        Success! The Books component is now rendering on the page.

        One of the great things about this Webpacker setup that we have got going is that if you edit the code in and save the file, the browser will automatically refresh. Go ahead and try it out now!

        The component is still working with data that we've coded in ourselves. The next piece of this puzzle is to configure the frontend code so that instead of pulling the data in from a hardcoded source, it pulls it in from the GraphQL API provided by Rails.

        We can do this using a JavaScript package called Apollo.

        Apollo

        The Apollo Client is a widely-used package that is used to provide an easy way of communicating between the frontend and a GraphQL API. We'll use this package to replace the hard-coded data within .

        Setting up Apollo

        To get started, we will need to add the and packages as a dependency. We can do that with this command:

        If you're running , make sure to restart it at this point to make sure it can load the new dependencies.

        Next, we will need to configure this Apollo Client to speak to our GraphQL API. We can do that by creating a new file at and putting this code inside it:

        This code does two main things.

        The first thing is that it defines , which sets the groundwork for how Apollo is configured to connect to our API. By using , and not passing it a URL, Apollo will default to making requests to -- which is exactly where our GraphQL API is hosted.

        This variable uses the from the page as well, ensuring that the requests pass the CSRF protections built into the for our Rails application. If we did not do this, in a production environment, users would not be able to make requests through to our GraphQL API as Rails would block their attempts due to null CSRF tokens being passed in.

        The second thing this code does is the variable. This function wraps a passed in component in the component, allowing that wrapped component to make calls to the GraphQL API.

        Using Apollo in our Books component

        With this code setup, we can now turn our attention back to . We want to convert this code to do a GraphQL query to load its data. We can start this process by defining a GraphQL query at the top of this file:

        To use this query, we can use the hook function from Apollo. We must first import it:

        Then we can use it inside the component:

        Note that for the most part, the API is the same. We are still using the variable, and the data is available at .

        The last thing to do here is to use the function to wrap the component.

        First, we'll need to import it.

        Then we can wrap our component when we export it:

        This will make it so that the component has access to the Apollo client we have configured, and that will mean the component will be able to run its GraphQL query.

        When we refresh the page now, we'll see that the data is being loaded from our API! We've now successfully connected our first React component back through to our GraphQL API.

        We're not completely done yet. There's one more issue hanging around. That issue is that the variable that is coming back from our query is completely untyped. I can see this in Visual Studio Code by hovering my cursor over . Here's what I see:

        data is you... no, it's any

        This is problematic, because it means that we're not having the properties we call on be typechecked. This means we can write this code:

        And TypeScript won't tell us that is not a part of the returned data. Further to this, the type is also, and the correctness of that data is only enforced by the earlier declaration we made earlier, and that we're still using in the component.

        We need to rectify this and ensure that we have accurate types from our data right from the moment they come out of the API responses.

        Sharing types between backend + frontend

        In this final section, we'll make it so that we can have our frontend read and understand the types specified by our backend. We will do this by using a combination of features from the gem, as well as a series of JavaScript packages from the family.

        When we write GraphQL queries, we must specify the types for all the fields. For example, over in we specify the types like this:

        These types are here so that we know what kind of data we're working with. We know that is going to be a string and that is also going to be returned as a string... except it's a string formatted as an timestamp as indicated by its special typing.

        We want these types in our React code so that we can be sure we're working with them correctly. But these types are in Ruby, not in JavaScript. So how do we get them out of Ruby, and into JavaScript?

        The way we can do this is with a Rake task built into the gem itself which will allow us to dump the GraphQL schema out to a particular file. This schema will be a JSON representation of our GraphQL API, and enables introspection of the API. If you've ever wondered how GraphQL apps like GraphQL Playground and GraphiQL know what fields are available, this is how! The application reads that schema, and from that, it will know the fields and their types.

        We can dump the schema for this application by using a Rake task that is built into the gem. That task needs some configuration so that it can find out where our Ruby schema is, and where to dump the JSON schema definitions to.

        Once we have that schema dumped, we can generate types from that dump by using a JavaScript package called .

        Dumping types from the GraphQL backend

        Let's start by dumping the types from the backend using a Rake task. Let's create this Rake task and specify its configuration, adding these lines to the within our application:

        The initializer is responsible for registering the Rake task with Rake. The option tells it where to find the schema, and this name must match the schema class name in . It's the name of your application, followed by the word "Schema", typically.

        The option tells this Rake task where we want to put the JSON schema definitions.

        And finally, tells the Rake task first to run the Rake task. This Rake task is responsible for loading the Rails application environment, and it will load the class as a part of that work.

        With the Rake task setup, we can now run it by running this command:

        This command will read our schema -- written in Ruby -- and convert it into a JSON file, which can be read by JavaScript.

        Generating types from the dumped schema

        Now that we have our schema dumped out, we can load it in using a package called . We will need to install this package first:

        Once this package has been installed, we can run another command to configure it. This configuration will ensure that Codegen works exactly as we want it for our application. We can kick off this configuration step by running:

        This command will prompt us for a few separate things:

        1. Application built with: Choose "React"
        2. Schema:
        3. Operations and fragments:
        4. Plugins: Leave default selected
        5. Output path:
        6. Generate introspection file: Choose "no" because the gem has done this already with the Rake task we just ran.
        7. Name config file: Leave it as the default,
        8. Script in package.json:

        Why not use a URL for schema?

        You might've noticed that the default option for "Schema" above is a URL. We

        couldput in http://localhost:3000/graphql here, and Codegen would still work. The only thing about that is that we must have a Rails server running at all times to have our Codegen command work.

        I think it's better having a schema file that is generated through a Rake task, and then you don't have to remember to run a Rails server. You might think differently, though! And that's alright too.

        This script will then install more packages that will assist us. These packages will appear within the list:

        • @graphql-codegen/typescript
        • @graphql-codegen/typescript-operations
        • @graphql-codegen/typescript-react-apollo

        These packages are the ones that will be used to generate the types for our TypeScript code. The series of packages will do this by reading from the file that was dumped from the Rake task. The way to make generate these types is by running the script :

        This command will read that file and generate TypeScript types into . Note that this will only read from the schema file generated by rather than constantly being updated by the backend automatically. So you will want to get into the habit of running both the Rake task and the Yarn command at the same time:

        Let's go through what each of these Codegen packages provides us, to better understand what we're getting out of using this tool.

        Codegen + TypeScript

        The first package, , generates TypeScript types from objects in the schema. If we go to , here's a couple of the types that we will see:

        First, there are some scalars that define shortcuts to common types. These are used a little further down when the type is defined to specify the types of fields that are available for objects according to our API. You will notice there that the property is specified with a question-mark. That means that this property can be missing completely from the returned object.

        These types provide us the groundwork for types in our application's TypeScript code. If we were to use these, we could leverage the shared types between the backend and the frontend. Our frontend couldn't use types that aren't correct according to the backend.

        But this picture is not complete without the two other packages here, so let's cover those first before we look into how we can use those types.

        Codegen + TypeScript + GraphQL Operations

        The second package, , generates TypeScript types from the operations and fragments we've specified in our TypeScript code. An example of an operation is this query in :

        The package scans our TypeScript files for these operations and will generate corresponding types for them. If we go to , we'll see the generated types for this query:

        The first type here says that the query does not take any variables. The second type specifies that when the returns data, it does so with a key called , and that the value for that key will be an array of items that are shaped as the type specifies, but just the field from that type -- as indicated by .

        This type can be used to ensure that we're only accessing data that our GraphQL query returns. The second type here specifies that yes the query does return an array of books... but it also says that the information that will come from that query only consists of the field. There is no other field there.

        We can use this second type in our code by specifying it as a generic in :

        This says to , that the type of data being returned by this function will match the shape specified by .

        If we look a little further down in our file, we'll see that the code is failing because we're trying to access the property from a object:

        Property 'id' does not exist

        We're seeing this message from TypeScript because our type for does not specify that books returned from that query have an property. TypeScript is smart enough to know that we're making a mistake here, and it quickly points it out!

        We can fix this by adding an field to the query in :

        And then by regenerating the types using:

        This last command updates the type to this:

        This is a type that now specifies that can have and specified. If we look back at the place where the error previously was occurring, we'll now see that it is fine:

        property id is fine

        This is the benefit that we get out of using . This package inspects the queries that are used in our application, and it will generate types from them. Those types can then be used by TypeScript to inform us if we're requesting correct or incorrect properties.

        Codegen + TypeScript + React + Apollo

        The final package that is added by Codegen's script is one called . This package brings together all of the previous parts into one very neat cohesive whole.

        This package does not provide us with types, but instead it provides us with functions. Specifically, it provides us with hooks that we can use in our React components to shortcut the necessity of specifying or as type arguments.

        Here's one of the functions it provides us:

        We can call this function in by first importing it:

        And then using it in place of our previous invocation in that same file:

        Isn't this much nicer than what we just had there?

        By invoking this function, we are getting Codegen to do the heavy lifting of specifying the type arguments for us. If we did not have Codegen doing this, then we would need to write it all out ourselves. A full query, including query variables, would need to be written like this:

        Sours: https://egghead.io/blog/rails-graphql-typescript-react-apollo


        299 300 301 302 303