Handling City Filter Uploads

Accept a CSV or XLSX of locations over an API, parse it into rows, keep only what falls inside the target city or radius, and store or reply with the result.

This tutorial builds an upload endpoint with a geo gate: a partner posts a file of locations, and only the rows relevant to your city ever reach storage. The same shape covers delivery zones, service areas and region-scoped imports. It assumes a registered endpoint; see Build Your First API. The geo side of this page runs offline against datasets on the node; background in Places and Geo Search.

1. Upload and Parse

function_get_file_from_form (get) takes the uploaded file off the multipart form by field name. Parse it into an array of row objects:

Only accepting the file for later? function_save_file_from_form (save) stores it as an asset instead of parsing it in the request.

2. The City Filter

Two blocks answer "is this row inside my area", depending on whether the area is a city or a drawn radius:

An uploaded file becomes rows: get takes the file from the form, parse CSV turns it into an array of objects, and filter by geo distance keeps only the rows whose location lies within their own radius of from city (here Breda). array length counts what survived.

Studio canvas example for the parse CSV block: upload, parse, filter by city.

On the canvas: geo_point_from_city (from city) resolves the target city to its centre point, and array_filter_geo_distance (filter by geo distance) keeps the rows whose own location field lies within reach. The block reads the location and the allowed distance from each row itself: location_key names the field holding the row's geo point, distance_key the field holding its radius. Rows carry their own reach, so a single pass handles "within 5 km" for one row and "within 500 m" for the next. What does not match comes out on out_of_range, so you can report or log the rejects instead of dropping them silently.

Rows that store plain lat/lng numbers instead of a geo point need one conversion first: geo_point_lat_lng_to_point (create). For a fixed radius applied to every row, test each row's point with condition_geo_is_point_in_circle (point in radius) inside an iterator, or filter at the storage level with query_bool_geo_distance so documents outside the area are never read at all.

3. Validate, Enrich, Store

Before the surviving rows reach storage:

  • Check each row's point with geo_point_validate (is valid): uploads regularly contain empty or swapped coordinates, and one bad row should not poison the batch.
  • Enrich what you keep with geo_point_reverse_geocode (reverse geocode), which resolves the point to street, city, province and country. Writing those onto the document with mutations_set_bp_field_multiple (set fields) makes the rows searchable by city name later, a combination production blueprints use directly on the upload path.
  • Write the batch with function_upsert_document_from_mutations (upsert document) inside a function_foreach (foreach) loop: re-uploading the same file then updates rows instead of duplicating them.

Reply with a small summary object: rows received, kept, rejected. Build it with object_new_fields (new object) fed by array_length (array length) on both output arrays, and send it with httpconnection_set_json (reply in JSON), the response backbone from Common Blueprint Patterns.

Notes From Practice

  • Large files do not belong in the request: parse, reply 202, and filter and store through function_custom_execute_from_queue (execute in queue). See Queue.
  • The geo blocks are offline lookups, so filtering ten thousand rows costs no external calls and no meaningful time.
  • Scope the endpoint and rate limit it; an open upload endpoint is an invitation. See Remote Access Control.
  • Branch on the parse block's error output before touching the array: replying 400 with "not a CSV" beats storing nothing and replying 200.
Places and Geo Search The offline dataset behind the geo filter blocks, and the search blocks built on it. File Upload & Processing Forms, assets and background pipelines for uploads in general. Common Blueprint Patterns The response backbone and error-handling shapes used here.

Frequently asked

How do I accept a CSV upload through a RUAL API?

Take the file off the multipart form with function_get_file_from_form, then parse it with file_parse_csv into an array of row objects. The parse block detects the separator and reports files that are not CSV on its error pin.

How do I keep only uploaded rows inside a city in RUAL?

Resolve the city with geo_point_from_city and filter the parsed rows with array_filter_geo_distance, which reads each row's location and allowed radius from fields you name. Rows that fall outside come out on the out_of_range pin, and all of it runs offline against datasets on the node.