Build Typed LinkedIn Services with Rest.li: PDL, Pagination, and Batch
Use LinkedIn Rest.li to generate typed Java stubs from PDL schemas, paginate with start and count, and bundle independent calls through POST /batch.
27 Jun 2026, 01:40 UTC

Start with the contract, not the JSON
When two services exchange JSON by hand, a renamed field or a missing null check surfaces at runtime. LinkedIn's Rest.li framework avoids that mismatch by generating Java interfaces and client stubs from a PDL (Platform Data Language) schema. PDL is LinkedIn's schema language for defining data structures. The useful takeaway: define your data and operations once in PDL, let Rest.li produce the server resource and client code, then call paginated endpoints with start and count or bundle unrelated calls in one POST /batch request.
Define a typed resource with PDL
A record declaration in PDL maps to a Java RecordTemplate subclass, which is a generated class representing a typed data record. A resource interface annotated with @Get, @Create, @Update, or @Delete exposes CRUD operations.
Example PDL schema, placed in your project's PDL source directory:
namespace com.example.thing
record UserProfile {
string urn,
string displayName,
optional string email
}
Rest.li's build plugin generates a UserProfile template and a resource interface. A server resource might use this illustrative, unverified structure:
@RestLiResource(name = "userProfiles")
public class UserProfileResource extends RestLiResource {
@Get
public UserProfile get(String urn) { ... }
@Create
public CreateResponse create(UserProfile profile) { ... }
}
Run the code-generation step from your project root with Gradle. Required permissions: read and write access to project files. Replace :restli with your plugin task name if it differs:
./gradlew :restli:compilePdls
Expected check: the build succeeds and generated sources appear under the project's generated-sources directory. Risk: annotation processing adds build time, and a mismatched plugin version can fail or generate stale stubs. Confirm the exact Gradle plugin and Java version in your Rest.li release notes before adopting this in production.
Paginate a collection
Rest.li paginates with PagingCollection, a wrapper that returns a slice of records plus PagingMetadata. PagingMetadata carries the total count and navigation context so callers know whether more pages exist.
Resource method signature, shown as illustrative structure only:
@Get
public PagingCollection<UserProfile> list(
@QueryParam("start") long start,
@QueryParam("count") int count) { ... }
Callers pass start as a zero-based offset and count as page size through query parameters. A practical verification: request start=0&count=10, confirm the response contains at most ten records, and inspect pagingMetadata.total against your expected record count. If start exceeds the total, expect an empty collection rather than an error.
Bundle independent calls with batch
Batch support lets a client send multiple resource operations in one HTTP POST to /batch. This reduces latency when fetching unrelated entities, such as a user profile and a set of connections in the same round trip.
A client call might bundle two gets, shown as illustrative structure only:
BatchRequest<UserProfile> request = new BatchRequest<>();
request.add("getProfile", client.getProfileBuilder().setUrn("urn:li:user:123").build());
request.add("getSettings", client.getSettingsBuilder().setUrn("urn:li:user:123").build());
BatchResponse<UserProfile> response = client.batch(request);
Diagnostic decision: use batch only for independent operations. If the second call depends on the first result, batch cannot help and sequential calls are clearer. Batch responses include per-operation status, so inspect each entry rather than assuming an HTTP 200 means every sub-request succeeded.
Limits and common mistakes
- Non-JVM callers cannot use generated stubs. Services written in Python, Go, or Node must craft JSON payloads that match the PDL schema by hand, which erodes the type-safety benefit.
- Schema changes require backward compatibility. Removing a field or changing a type can break generated clients and servers at runtime. Follow Rest.li compatibility rules and coordinate deploys.
- Build complexity increases. Annotation processing and code generation are heavier than plain Spring MVC controllers. Measure build times before adopting Rest.li for small services.
- Do not treat
/batchas a transaction. Operations run independently; one failure does not roll back another. - Pagination defaults vary. Do not assume a maximum
count; check the resource implementation or documentation for page-size caps.
Verify before you deploy
- Compile a sample PDL file with the Rest.li plugin and confirm resource interfaces, client stubs, and
PagingMetadataclasses are generated. - Call a paginated endpoint with
startandcountand verify the response includes total count and the correct subset of records. - Send a two-operation batch request and confirm each sub-response contains the expected status and body.
This is a conservative draft for human review: confirm current Rest.li versions, plugin syntax, and annotation behavior against the project's documentation before using the examples in production.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.