Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Router v2 #326

Merged
merged 71 commits into from
Jan 28, 2025
Merged

Router v2 #326

merged 71 commits into from
Jan 28, 2025

Conversation

oscartbeaumont
Copy link
Member

@oscartbeaumont oscartbeaumont commented Dec 2, 2024

Goal: Introduce the new router, make it supporting by integrations (rspc_axum/rspc_tauri) and allow converting a legacy router into it.

General design:

  • Dropped TMeta generic in favor of improved solution in upcoming middleware system. This has always been a half-baked feature.
  • Procedure names now must be fully unqiue. Previously they were namespaces by the type (query/mutation/subscription) but that is no longer the case. You will recieve an error if you don't comply with this.
  • Using the new specta::Language trait for exporting, opening up the door for easily supporting other langauges. lol goodbye specta::Language you won't be missed.
  • Note that the new router isn't directly usable at the moment. It can be converted fromthe older router but can't mount procedures directly as we are going to introduce a new syntax and middleware system in the near future.
  • Dropped Router::config. You must export types via the new router with the new bindings format.
  • New bindings format - explain it? Must change Bindings to BindingsLegacy to use the existing clients.
  • rspc_axum::endpoint now takes rspc::Router2 (you need it for the types anyway)
  • Drop tracing feature with no directly replacement.
  • rspc_tauri::plugin to rspc_plugin_rspc::init (rspc-tauri to tauri-plugin-rspc (remember to yank old crate), and plugin to init)
  • Dropping HTTP integrations in favor on them being manually implemented by the user.

Breaking changes:

  • Requires new Specta release
  • All procedure names must be unique regardless of type.
  • let (routes, types) = Router2::from(legacy_router).build().unwrap(); (must be used for the runtime and the types)
  • Router::config has been removed. You can use types.export_to.
  • rspc_axum::endpoint(routes). Now takes routes instead of rspc::Router.
  • Drop jsonrpc executor API with no real replacement for now
  • Must rename Procedures to ProceduresLegacy in your TS code.
  • Add location field to Procedure
  • Completly new Tauri integration
  • The user is required to enable the legacy feature to get the old bindings format.

For my future reference:
The HTTP interface has been refactored to run on rspc_core::Procedure which is designed around the queries path not being namespaced by the type this means keeping full backwards compatibility is really hard. Given we are going to change the bindings format with Router2 and upgrade the clients to the new stuff first we may as well just do this as a breaking change. The old system being Box<dyn Layer> and Arc<Router> makes it tough to do conversions too.

Unsorted:

  • Upgrade dependencies
  • Improve Procedure's Arcing.
  • Should Procedure2::error being Option<DataType> or not?
  • Ability to downcast instead of Serialize on error. Similar to the regular Ok type.

Remaining questions:

  • rspc_devtools - Should we provide a way to set CORS header. If we leave it on the user the URL being the same is a problem.

Content-Type's within procedures for non-Serde stuff maybe - trpc/trpc#3468
- Allows types to match properly

Defining middleware which don't depend on rspc crate.

This PR will be merged but work will continue as #328, #332 and specta-rs/website#9.

@oscartbeaumont oscartbeaumont marked this pull request as draft December 2, 2024 15:38
@oscartbeaumont
Copy link
Member Author

oscartbeaumont commented Dec 19, 2024

Completed:

  • Router: TryFrom<legacy::Router> (maybe use Try and panic cause we will error in build longer term)
  • Router: Default
  • Router: Debug
  • Router::nest
  • Router: Iterator/IntoIterator
  • Router::merge
  • Basic type exporting
  • New bindings support for nested routers
  • Giving Procedure to multiple integrations (Eg. Tauri and Axum) the Iterator makes this potentially problematic.
  • Export the legacy bindings format from a Router2
  • Make Axum work with Router2. Can we do this without breaking changes and switch endpoint to use it too?
  • Error handling in layer_to_procedure
  • Switch out rspc_axum endpoint backend to the new stuff
  • rspc_tauri support
  • rspc_core metadata for publishing + logo in lib.rs
  • Drop jsonrpc executor API
  • Remove rspc dependency from rspc_axum and rspc_tauri to make future stuff easier.
  • Customise the TypeCollection's header. It should says rspc not Specta!
  • Worskspace Cargo lints
  • Virtual workspace like main-rewrite
  • Typescript to Rust jump to definition by abusing source map (mannnn this is cool!)
  • Cleanup ProcedureStream
  • ProcedureStream::next2 vs ProcedureStream::next
  • Tauri use Channel and expose WindowHandle
  • Migrate all middleware and split to integrations directory?
  • ProcedureStream::from_stream_value
  • Fix invalidation middleware in Axum example
  • Drop ProcedureError::Serialize & generic
  • Handle serialization errors in rspc-tauri (don't just unwrap like we do now)
  • Catch panics in ProcedureStream
  • DynInput<'a, 'de> should really be &'a Input<'de>` but that's hard.
  • Consistency between routes and Procedures
  • Concider changing Into<Procedures<TCtx>> to AsRef so it can be .into()ed multiple times then maybe remove Clone impl on Routes?
  • Error handling for duplicate procedure names (Eg. give Router::build an error type)
  • Can Error::serialize return value instead of taking serializer?
  • Break out rspc part of Axum example
  • Request context so we can track queries requiring invalidation. Gonna use TCtx.
  • ProcedureBuilder needs TBaseInput and TBaseResult
  • Procedure2 needs TInput and TResult derived from TBaseX on the ProcedureBuilder
  • IntoMiddleware needs TBaseX
  • Fix ProcedureBuilder::with
  • Introduce ErasedProcedure.
  • Extension as a clone of Middleware.
  • ErasedProcedure::with which takes Extension
  • Procedure2 needs Into<ErasedProcedure<TCtx>> and Router::procedure needs to take that as it's argument.
  • ProcedureStream::should_flush and optional manual stream flushing mechanism like what Mattrax has.
  • Error::code or Error::status. What should we call it?
  • Sort out tracing calls in crates/*
  • drop axum::Endpoint and move the handler into userspace
  • drop status codes completly -> ProcedureStream::next_status and ResolverError::status
  • drop GET/POST /rspc/{procedure_name} and make it all one endpoint POST /rspc.
    • Request is FormData keyed on procedure name and value as the input.
    • Userspace will decode and spawn onto a runtime.
    • Allow rspc_invalidation to add arbitrary queries into the response.
  • ProcedureStream::resolved
  • ProcedureStream::flushable
  • finish ProcedureStream::map and expose all related types in public API
  • Moving State to rspc_core and exposing it on rspc_core::Procedures.
  • Make rspc-openapi an extension

@oscartbeaumont
Copy link
Member Author

oscartbeaumont commented Dec 22, 2024

The following needs to end up in documentation somewhere.

We will delay running SFM invalidated queries until all items in the batch are complete.
This is to ensure we don't have conflicting keys in the response.
The client is expected to send mutations by themselves (not in a batch) but this can't be enforced by the server so we do the above as a fallback.

For queries and subscriptions calling .invalidate is only allowed if streaming hasn't started (as we delay SFC queries until then).

The response body needs to key items something like:

pub enum Key {
    Response(u32), // Reference to the request
    SFM(String, serde_json::Value), // SFM key (procedure name and input)
}

type ResponseBody = HashMap<Key, Value>;

This way we will never have problems with running a SFM query that was also including in the request batch.
The client would be expected to queue resolving promises so the SFM wins as the canonical value.

@oscartbeaumont oscartbeaumont mentioned this pull request Dec 25, 2024
7 tasks
@oscartbeaumont oscartbeaumont merged commit 0c9cb22 into main Jan 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants