Rails pagination in API using Her, Faraday

I try to figure it out all day and it drives me crazy.

I have two rail applications, ServerApp and ClientApp. ClientApp receives data from ServerApp through the API using its gem. Everything was great until I needed the pagination information.

This is the method that I use to receive orders (Kanimari is used for searching for pagination and search):

# ServerApp def search @search = Order.includes(:documents, :client).order('id desc').search(params[:q]) @orders = @search.result(distinct: true).page(params[:page]).per(params[:per]) respond_with @orders.as_json(include: :documents) end 

It returns an array of hashes in json, which it uses as a set of orders. It works great.

 # Response [ { "client_id": 239, "created_at": "2013-05-15T15:37:03-07:00", "id": 2422, "ordered_at": "2013-05-15T15:37:03-07:00", "origin": "online", "updated_at": "2013-05-15T15:37:03-07:00", "documents": [ { ... } ] }, ... ] 

But I need pagination information. It looks like I need to send it as metadata using json . So I change my answer to this:

 respond_to do |format| format.json do render json: { orders: @orders.as_json(include: :documents), metadata: 'sent' } end end 

It really sends metadata, so in my ClientApp I can write @ orders.metadata and get "sent". But now my orders are nested in an array inside the "orders", so I need to use @ orders.orders, and then it treats it as an array, not its collection.

After some reading, it seemed like sending the paginated information through the headers was the same as many other people did (I was able to get the headers configured in after_filter using this guide ). But I lost even more information on how to get these response headers in my ClientApp - I believe that I need Faraday middleware, but I just could not get it to work.

If anyone knows how I can do this, I would be very grateful. I can’t take another day by hitting my head against the wall, but I feel that I am only one important information from this!

+6
source share
3 answers

I ran into the same problem and solved it by adding my own middleware and rewriting the parsing and on_complete methods without any problems and avoiding the use of global variables.

Here is the code:

  class CustomParserMiddleware < Her::Middleware::DefaultParseJSON def parse(env) json = parse_json(env[:body]) pagination = parse_json(env[:response_headers][:pagination_key]) || {} errors = json.delete(:errors) || {} metadata = json.delete(:metadata) || {} { :data => json, :errors => errors, :metadata => { :pagination => pagination, :additional_metadata => metadata }, end def on_complete(env) env[:body] = case env[:status] when 204 parse('{}') else parse(env) end end end 

then you can access the pagination as follows:

  model = Model.all model.metadata[:pagination] 
+6
source

I finally got this job. The trick was to use the global variable in faraday on_complete - I tried to find the best solution, but it was the best I could do. Once again, I got the header code here . Here's a complete guide on how to get the pagination with it:

First, on my server side, I have a Kaminari stone, and I pass page and per as parameters to the server from the client. (It also uses a search to search)

 def search @search = Order.order('id desc').search(params[:q]) @orders = @search.result(distinct: true).page(params[:page]).per(params[:per]) respond_with @orders.as_json(include: :items) end 

My client makes a request as follows:

 @orders = Order.search(q: { client_id_eq: @current_user.id }, page: params[:page], per: 3)` 

Returning to the server, I have this in my ApiController (application controller for api):

 protected def self.set_pagination_headers(name, options = {}) after_filter(options) do |controller| results = instance_variable_get("@#{name}") headers["X-Pagination"] = { total_count: results.total_count, offset_value: results.offset_value }.to_json end end 

In the orders_controller.rb server, I set the pagination headers for the search method:

 class OrdersController < ApiController set_pagination_headers :orders, only: [:search] ... end 

Now, to get the headers, we need the Faraday middleware in it on the client.

 # config/initializers/her.rb Her::API.setup url: Constants.api.url do |c| c.use TokenAuthentication c.use HeaderParser # <= This is my middleware for headers c.use Faraday::Request::UrlEncoded c.use Her::Middleware::DefaultParseJSON c.use Faraday::Adapter::NetHttp c.use Faraday::Response::RaiseError end # lib/header_parser.rb # don't forget to load this file in application.rb with something like: # config.autoload_paths += Dir[File.join(Rails.root, "lib", "*.rb")].each { |l| require l } class HeaderParser < Faraday::Response::Middleware def on_complete(env) unless env[:response_headers]['x-pagination'].nil? # Set the global var for pagination $pagination = JSON.parse(env[:response_headers]['x-pagination'], symbolize_names: true) end end end 

Now on your client controller you have a global hash variable called $ pagination; mine is as follows:

 $pagintation = { total_count: 0, offset_value: 0 }` 

Finally, I added the Kaminari gem to my client application to split the array and get these simple page links:

 @orders = Kaminari.paginate_array(@orders, total_count: $pagination[:total_count]).page(params[:page]).per(params[:per_page])` 

I hope this can help someone else, and if anyone knows a better way to do this, let me know!

+2
source
0
source

Source: https://habr.com/ru/post/945215/


All Articles